野牛 3.8.1

下一篇: , 上一篇: (dir) [目录][索引]

野牛

本手册(2021 年 9 月 10 日)适用于 GNU Bison(版本 3.8.1), GNU 解析器生成器。

版权所有 © 1988–1993、1995、1998–2015、2018–2021 免费 软件基金会

授予复制、分发和/或修改本文档的权限 GNU 自由文档许可证 1.3 版或更高版本的条款 由自由软件基金会发布的版本;没有不变性 部分,封面文本为“GNU 手册”,以及 封底文字如下(a)所示。许可证的副本包含在 标题为“GNU 自由文档许可证”的部分。

(a) 自由软件基金会的封底文字是:“您可以自由复制和修改 这本 GNU 手册。从 FSF 购买副本支持它开发 GNU 并促进软件自由。

目录


下一篇: , 上一篇: 牛, 上一篇: 野牛 [内容][索引]

介绍

Bison 是一个通用的解析器生成器,用于转换带注释的 将上下文无关语法转换为确定性 LR 或广义 LR (GLR) 解析器 使用 LALR(1)、IELR(1) 或规范的 LR(1) 解析器表。一旦你是 精通野牛,你可以用它来发展广泛的语言 解析器,从简单的桌面计算器到复杂编程中使用的解析器 语言。

Bison 向上兼容 Yacc:所有正确编写的 Yacc 语法 应该与野牛一起工作,没有任何变化。任何熟悉 Yacc 的人都应该 能够毫不费力地使用 Bison。您需要精通 C、C++、 D或Java编程,以便使用Bison或理解本手册。

我们从教程章节开始,解释 使用 Bison 并显示三个解释示例,每个构建在 最后。如果您不了解 Bison 或 Yacc,请从阅读这些开始 章。以下是描述具体方面的参考章节 野牛的细节。

《野牛》最初由罗伯特·科贝特(Robert Corbett)撰写。理查德·斯托曼(Richard Stallman)制作 它与 Yacc 兼容。卡内基梅隆大学的威尔弗雷德·汉森(Wilfred Hansen) 添加了多字符字符串文本和其他功能。从那时起, Bison 变得更加强大并发展了许多其他新功能,这要归功于 感谢一长串志愿者的辛勤工作。有关详细信息,请参阅 Bison 中包含的 和 文件 分配。THANKSChangeLog

此版本对应于 Bison 的 3.8.1 版本。


下一篇: , 上一篇: , 上一篇: 野牛 [内容][索引]

使用Bison的条件

Bison 生成的解析器的分发条款允许使用解析器 在非自由程序中。在 Bison 版本 2.2 之前,这些额外权限 仅当 Bison 在 C 语言中生成 LALR(1) 解析器时才适用。和之前 Bison 版本 1.24,Bison 生成的解析器只能在程序中使用 那是自由软件。

其他 GNU 编程工具,如 GNU C 编译器,从未有过 这样的要求。它们总是可以用于非自由软件。这 野牛与众不同的原因不是由于特殊的政策决定;它 由于将通常的通用公共许可证应用于所有野牛 源代码。

Bison 实用程序的主要输出 — Bison 解析器实现 文件 - 包含一大块野牛的逐字副本,即 解析器实现的代码。(语法中的操作是 在某一时刻插入到此实现中,但其余大部分 实现不会更改。当我们将 GPL 术语应用于 骨架代码的实现,效果是限制 使用 Bison 输出到自由软件。

我们没有改变条款,因为对那些想要制作的人表示同情 软件专有。软件应该是自由的。但我们得出结论 将 Bison 的使用限制在自由软件上并没有起到什么作用 人们使其他软件免费。因此,我们决定将 使用Bison的条件与使用Bison的实际条件相匹配。 其他 GNU 工具。

当 Bison 为解析器生成代码时,此例外适用。您可以 通过检查 以“作为特殊例外...”开头的文本的文件。文本 详细说明例外的确切条款。


下一篇: , 上一篇: , 上一篇: 野牛 [内容][索引]

GNU 通用公共许可证

版本 3, 29 June 2007
Copyright © 2007 Free Software Foundation, Inc. https://fsf.org/

Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.

序言

GNU 通用公共许可证是一个免费的 copyleft 许可证,用于 软件和其他类型的作品。

大多数软件和其他实际作品的许可证都是设计的 剥夺你分享和改变作品的自由。相比之下, GNU通用公共许可证旨在保证您的自由 共享和更改程序的所有版本,以确保它保持不变 为所有用户提供免费软件。我们,自由软件基金会, 我们的大多数软件都使用 GNU 通用公共许可证;它 也适用于其作者以这种方式发布的任何其他作品。你 也可以将其应用于您的程序。

当我们谈论自由软件时,我们指的是自由,而不是 价格。我们的通用公共许可证旨在确保您 可以自由地分发自由软件的副本(并收取费用 如果您愿意,他们会收到源代码,或者如果您可以获得源代码 想要它,您可以更改软件或在新的软件中使用它的一部分 免费程序,并且你知道你可以做这些事情。

为了保护您的权利,我们需要防止他人拒绝您 这些权利或要求您放弃这些权利。因此,您 如果您分发 软件,或者如果您修改它:尊重自由的责任 其他人。

例如,如果您分发此类程序的副本,则是否 免费或收费,您必须将相同的内容传递给收件人 你所获得的自由。你必须确保他们也 接收或可以获取源代码。你必须向他们展示这些 条款,以便他们了解自己的权利。

使用 GNU GPL 的开发人员通过两个步骤来保护您的权利: (1) 主张软件的版权,以及 (2) 向您提供本许可 授予您复制、分发和/或修改其法律许可。

为了保护开发者和作者,GPL 清楚地解释了 此免费软件不提供任何保证。对于用户和 为了作者的缘故,GPL 要求修改后的版本标记为 改变,这样他们的问题就不会被错误地归咎于 以前版本的作者。

某些设备旨在拒绝用户访问安装或运行 其中软件的修改版本,尽管 制造商可以这样做。这从根本上与 旨在保护用户更改软件的自由。这 这种滥用的系统模式发生在产品领域 个人使用,这正是最不可接受的地方。 因此,我们设计了这个版本的 GPL 来禁止 练习这些产品。如果此类问题在 其他领域,我们随时准备将这一规定扩展到那些 域,以保护 用户的自由。

最后,每个程序都不断受到软件专利的威胁。 国家不应允许专利限制开发和使用 通用计算机上的软件,但在那些这样做的计算机上,我们希望 避免专利适用于自由程序的特殊危险 可以使它有效地专有化。为了防止这种情况,GPL 确保专利不能用于使程序成为非自由的。

复制、分发和 修改如下。

条款及细则

  1. 定义。

    “本许可证”是指 GNU 通用公共许可证的第 3 版。

    “版权”也指适用于其他种类的类似版权的法律 的作品,例如半导体掩模。

    “本程序”是指根据本协议获得许可的任何受版权保护的作品 许可证。每个被许可人都称呼为“您”。“被许可人”和 “接收者”可以是个人或组织。

    “修改”作品是指复制或改编全部或部分作品 以需要版权许可的方式,而不是制作 一模一样的副本。由此产生的作品称为 早期作品或“基于”早期作品的作品。

    “涵盖的作品”是指未经修改的程序或基于作品的作品 在程序上。

    “传播”一部作品意味着用它做任何事情,没有 许可,将使您直接或次要承担以下责任 根据适用的版权法侵权,除非在 计算机或修改私人副本。传播包括复制、 分发(有或没有修改),提供给 公共活动,在一些国家还有其他活动。

    “传达”作品是指任何一种传播,使其他作品成为可能 制作或接收副本的当事人。仅仅与用户互动 通过计算机网络,没有传输副本,不是 输送。

    交互式用户界面显示“适当的法律声明” 它包括方便和突出可见的程度 (1) 显示适当的版权声明的功能,以及 (2) 告诉用户对工作没有保证(除了 在提供保证的范围内),被许可人可以传达 在本许可证下工作,以及如何查看本许可证的副本。如果 该界面显示用户命令或选项的列表,例如 菜单,列表中的突出项目符合此标准。

  2. 源代码。

    作品的“源代码”是指作品的首选形式 对其进行修改。“目标代码”是指任何非源形式 的作品。

    “标准接口”是指官方接口 由公认的标准机构定义的标准,或者,在以下情况下 为特定编程语言指定的接口,一种 在使用该语言的开发人员中广泛使用。

    可执行作品的“系统库”包括任何内容,其他 比整个作品,即 (a) 包含在正常形式中 打包一个主要组件,但不属于该主要组件 组件,并且 (b) 仅用于使工作能够使用该组件 主要组件,或实现标准接口,其 实现以源代码形式向公众开放。一个 在这种情况下,“主要组成部分”是指一个主要的基本组成部分 (内核、窗口系统等)的特定操作系统 (如果有)运行可执行工作,或用于 生成工作,或用于运行它的目标代码解释器。

    目标代码形式作品的“对应来源”是指所有 生成、安装和 (对于可执行文件) 所需的源代码 work)运行目标代码并修改工作,包括脚本 控制这些活动。但是,它不包括作品的 系统库,或通用工具,或正式发布的免费工具 在执行这些活动时未修改的程序,但 这些都不是工作的一部分。例如,对应源 包括与源文件关联的接口定义文件 共享库的工作和源代码,并动态地 工作专门设计为需要的链接子程序, 例如通过亲密的数据通信或控制流之间的那些 子程序和工作的其他部分。

    相应的来源不需要包含用户可以包含的任何内容 从相应源的其他部分自动再生。

    源代码形式作品的相应来源是相同的 工作。

  3. 基本权限。

    根据本许可授予的所有权利均在以下期限内授予 程序的版权,并且不可撤销,前提是声明 满足条件。本许可明确确认您的无限 运行未修改程序的权限。运行 所涵盖的工作仅受本许可证的保护,前提是其 内容,构成涵盖作品。本许可证承认您的 版权法规定的合理使用权或其他等效权利。

    您可以制作、运行和传播您不传达的涵盖作品, 无条件,只要您的许可证仍然有效。 您可以将涵盖的作品转让给他人,其唯一目的是拥有 他们专门为您进行修改,或为您提供 运行这些工程的设施,前提是您遵守 本许可的条款,用于传达您不这样做的所有材料 控制版权。因此制作或运行所涵盖作品的人 您必须完全代表您,在您的指导下这样做,并且 控制,禁止他们复制您的任何副本 他们与您的关系之外的受版权保护的材料。

    在任何其他情况下,仅在 条件如下。不允许再许可;第10节 使它变得没有必要。

  4. 保护用户的合法权利免受反规避法的影响。

    任何涵盖的工作均不应被视为有效技术的一部分 根据任何适用法律采取的措施,履行第条规定的义务 1996年12月20日通过的《世界知识产权组织版权条约》第11条,或 禁止或限制规避此类行为的类似法律 措施。

    当您转让涵盖的作品时,您放弃任何禁止的法律权力 规避技术措施的程度 通过行使本许可项下的权利来实现规避 尊重所涵盖的工作,并且您否认有任何限制的意图 作为强制执行的手段对作品的操作或修改,反对 作品的使用者、您或第三方的合法禁止权 规避技术措施。

  5. 逐字传送副本。

    您可以作为您一字不差地传达本程序源代码的副本 在任何媒介上接收它,前提是您醒目地和 在每份副本上适当发布适当的版权声明; 保持所有声明完好无损的通知,说明本许可证和任何 根据第7条增加的非许可条款适用于本守则; 保持所有关于没有任何保证的通知的完整性;并全力以赴 接收本许可证的副本以及程序。

    您可以对您传达的每份副本收取任何价格或无价格, 您可以付费提供支持或保修保护。

  6. 传送修改后的源版本。

    您可以根据本程序或对 从程序中生成它,以源代码的形式在 第 4 节的条款,前提是您还满足所有这些条件 条件:

    1. 作品必须带有醒目的通知,说明您对其进行了修改, 并给出相关日期。
    2. 该作品必须带有醒目的通知,说明其已发布 根据本许可和第 7 节添加的任何条件。这 要求将第 4 节中的要求修改为“保持所有 通知“。
    3. 您必须根据本许可证将整个作品作为一个整体许可给 任何拥有副本的人。本许可证将 因此,连同任何适用的第 7 条附加条款一起适用, 到整个作品及其所有部分,无论它们如何 被包装好。本许可证不授予许可作品的权限 任何其他方式,但如果您有 单独收到了它。
    4. 如果作品具有交互式用户界面,则每个界面都必须显示 适当的法律声明;但是,如果程序具有交互式 不显示适当法律声明的界面,您的作品 不需要让他们这样做。

    涵盖作品与其他独立和独立作品的汇编 作品,就其性质而言不是所涵盖作品的延伸, 并且不与之结合,例如形成一个更大的程序, 在存储或分发介质的体积中或卷上,称为 “聚合”,如果汇编及其产生的版权不是 用于限制编译用户的访问或合法权限 超出个人工作允许的范围。纳入涵盖的作品 在总体上不会导致此许可证适用于其他许可证 骨料的一部分。

  7. 传达非源形式。

    您可以根据以下条款以目标代码形式传达涵盖的作品 第 4 节和第 5 节,前提是您还传达了机器可读的内容 根据本许可条款的相应来源,在以下之一中 方式:

    1. 将目标代码传达到物理产品中或体现在物理产品中 (包括物理分发介质),并伴有 相应的源通常固定在耐用的物理介质上 用于软件交换。
    2. 将目标代码传达到物理产品中或体现在物理产品中 (包括物理分发介质),并附有书面 优惠,有效期至少三年,有效期与您 为该产品型号提供备件或客户支持,以提供 任何拥有目标代码的人 (1) 一份 产品中所有软件的对应来源 受本许可证保护,在通常使用的耐用物理介质上 对于软件交换,价格不超过您的合理价格 实际执行源传输的成本,或 (2) 访问 从网络服务器免费复制相应的源。
    3. 将目标代码的单个副本与书面代码的副本一起传达 提供相应的来源。这个替代方案是 仅允许偶尔和非商业性地使用,并且仅在您 根据小节,收到了具有此类报价的目标代码 6b.
    4. 通过提供从指定位置的访问来传达目标代码 (免费或收费),并提供对 对应的来源以相同的方式通过同一地点 进一步收费。您无需要求收件人复制 相应的源以及目标代码。如果复制的地方 目标代码为网络服务器,对应源可以是 在支持以下支持的其他服务器(由您或第三方操作)上 同等的复印设施,前提是您保持清晰的方向 在目标代码旁边,说明在哪里可以找到相应的源。 无论哪个服务器托管相应的源,您都保持不变 有义务确保它在需要时可用 满足这些要求。
    5. 使用点对点传输来传达目标代码,前提是 通知其他对等方目标代码和对应来源 该作品正在免费向公众提供 第6d款.

    目标代码的可分离部分,其源代码被排除在外 从相应的源作为系统库,不需要 包含在传达目标代码工作中。

    “用户产品”是 (1) “消费品”,即任何 有形个人财产,通常用于个人、 家庭或家庭用途,或 (2) 任何设计或销售的物品 并入住宅。在确定产品是否是 消费品,疑问案件应以有利于 覆盖。对于特定用户收到的特定产品, “通常使用”是指该类的典型或常见用途 产品,无论特定用户的状态或方式如何 特定用户实际使用、期望或预期 使用,产品。无论产品是消费品 产品是否具有实质性的商业、工业或 非消费者用途,除非此类用途是唯一重要的用途 产品的使用方式。

    用户产品的“安装信息”是指任何方法, 过程、授权密钥或所需的其他信息 在该用户中安装并执行涵盖作品的修改版本 产品来自其相应来源的修改版本。这 信息必须足以确保 修改后的目标代码在任何情况下都不会被阻止或干扰 仅仅因为进行了修改。

    如果您传达目标代码,请在本节中、或与或 专门用于用户产品,并且输送发生为 交易的一部分,其中占有和使用权 用户产品永久转让给收件人或转让给收件人 固定期限(无论交易的特征如何),, 根据本节传达的相应来源必须随附 通过安装信息。但此要求不适用 如果您或任何第三方均不保留安装能力 修改了用户产品上的目标代码(例如,作品具有 已安装在ROM中)。

    提供安装信息的要求不包括 要求继续提供支持服务、保修或 已修改或安装的作品的更新 接收者,或已修改的用户产品,或 安装。修改时,可能会拒绝对网络的访问 本身对网络的运行产生重大不利影响 或违反跨 网络。

    传达的相应来源,并提供安装信息, 根据本节的规定,必须采用公开的格式 记录在案(并在 源代码形式),并且必须不需要特殊的密码或密钥 开箱、阅读或复印。

  8. 附加条款。

    “附加权限”是补充本条款的条款 通过对一个或多个条件进行例外处理来获得许可。 适用于整个程序的其他权限应 在一定程度上,应被视为包含在本许可中 它们在适用法律下有效。如果其他权限 仅适用于本程序的一部分,该部分可以单独使用 在这些权限下,但整个程序仍受 本许可证不考虑其他权限。

    当您转交涵盖作品的副本时,您可以自行选择 从该副本或 它。(可以编写其他权限以要求自己的权限 在某些情况下,当您修改作品时,请将其删除。您可以放置 您添加到涵盖作品中的材料的其他权限, 您拥有或可以给予适当的版权许可。

    尽管本许可有任何其他规定,但对于材料,您 添加到涵盖的作品中,您可以(如果获得版权所有者的授权) 该材料)用以下条款补充本许可的条款:

    1. 以不同于条款的方式放弃保证或限制责任 本许可证第 15 条和第 16 条;或
    2. 要求保存指定的合理法律声明或作者 该材料或相应法律声明中的归属 由包含它的作品展示;或
    3. 禁止歪曲该材料的来源,或 要求此类材料的修改版本在 与原始版本不同的合理方式;或
    4. 限制出于宣传目的使用许可人名称或 材料的作者;或
    5. 拒绝根据商标法授予使用某些商品的权利 名称、商标或服务标志;或
    6. 要求对该材料的许可人和作者进行赔偿 任何传达材料(或其修改版本)的人 对接收方承担任何责任的合同责任 这些合同假设直接强加给那些人的责任 许可方和作者。

    所有其他非允许的附加条款均被视为“进一步 限制“,符合第10条的含义。如果程序作为您 收到它或其任何部分包含通知,说明它是 受本许可证以及进一步的条款的约束 限制,您可以删除该术语。如果许可证文档包含 进一步的限制,但允许在此下重新许可或转让 许可,您可以添加到受条款约束的涵盖工作材料中 该许可文件,前提是进一步的限制确实 在此类重新许可或转让后无法幸存。

    如果您根据本节的规定为涵盖的作品添加条款,则您 必须在相关的源文件中放置 适用于这些文件的附加条款,或指示 在哪里可以找到适用的条款。

    其他条款,无论是允许的还是非允许的,都可以在 单独书面许可的形式,或声明为例外;这 上述要求适用于任何一种方式。

  9. 终止。

    除非明确规定,否则您不得传播或修改涵盖的作品 根据本许可证提供。任何以其他方式传播或 修改它是无效的,并将自动终止您在以下权利 本许可(包括根据第三条授予的任何专利许可 第11条)。

    但是,如果您停止所有违反本许可证的行为,则您的许可证 (a)暂时恢复特定版权所有者的版权, 除非并且直到版权所有者明确并最终 终止您的许可,并且 (b) 永久终止,如果版权所有者 未能在以下情况下通过某种合理方式通知您违规行为 戒烟后 60 天。

    此外,您从特定版权所有者处获得的许可是 如果版权所有者通知您 通过一些合理的手段违规,这是你第一次有 收到违反本许可(任何作品)的通知 版权所有者,并且您在 30 天后纠正违规行为 您收到通知。

    终止您在本节项下的权利并不终止 根据 本许可证。如果您的权利已被终止,而不是永久终止 恢复后,您没有资格获得相同的新许可证 第 10 节下的材料。

  10. 拥有副本不需要接受。

    您无需接受此许可证即可接收或运行 程序的副本。涵盖作品的辅助传播 仅因使用点对点传输而发生 同样,接收副本也不需要接受。然而 除本许可证外,其他任何内容均授予您传播或 修改任何涵盖的工作。如果您这样做,这些行为将侵犯版权 不接受此许可证。因此,通过修改或传播 涵盖的工作,您表示您接受本许可。

  11. 下游收件人的自动许可。

    每次您传达涵盖的作品时,收件人都会自动 从原始许可方获得许可证,以运行、修改和 传播该作品,但须遵守本许可证。您不承担责任 用于强制第三方遵守本许可证。

    “实体交易”是指转让对 组织,或实质上所有资产,或细分一个 组织或合并组织。如果传播覆盖 工作源于实体交易,每一方都参与其中 收到作品副本的交易也会收到任何东西 当事人前任所涉作品的许可 根据前款给予,加上占有 感兴趣的前任的相应作品来源,如果 前任拥有它或可以通过合理的努力获得它。

    您不得对行使 根据本许可授予或确认的权利。例如,您可以 不对行使 根据本许可授予的权利,您不得提起诉讼 (包括诉讼中的交叉申索或反申索)指称 任何专利权利要求均因制造、使用、销售、提供而受到侵犯 出售或导入程序或其任何部分。

  12. 专利。

    “贡献者”是授权根据本协议使用的版权所有者 程序或程序所基于的作品的许可。这 这样许可的作品称为贡献者的“贡献者版本”。

    贡献者的“基本专利权利要求”是所有拥有的专利权利要求 或由贡献者控制,无论是已经获得的还是 此后获得的,将以某种方式被侵犯,允许 根据本许可,制作、使用或销售其贡献者版本, 但不包括仅作为 进一步修改贡献者版本的结果。为 就本定义而言,“控制”包括授予的权利 以符合以下要求的方式进行专利再许可 本许可证。

    每个贡献者都授予您非排他性、全球性、免版税 根据贡献者的基本专利权利要求,专利许可,以 制造、使用、出售、要约出售、导入和以其他方式运行、修改和 传播其贡献者版本的内容。

    在以下三段中,“专利许可”是任何明示的 不执行专利的协议或承诺,无论名称如何 (例如明确允许实施专利或承诺不 起诉专利侵权)。将此类专利许可“授予”给 当事方是指作出此类协议或承诺不执行 对当事人不利的专利。

    如果您在知情的情况下传达涵盖的作品,则依赖专利许可, 并且任何人都无法获得作品的相应来源 根据本许可的条款,通过以下方式免费复制 公开可用的网络服务器或其他易于访问的方式, 那么你必须 (1) 使相应的来源如此 可用,或 (2) 安排剥夺您自己从 本特定作品的专利许可,或 (3) 以某种方式安排 根据本许可的要求,延长专利 许可给下游接收者。“故意依赖”意味着你有 实际知道,但对于专利许可,您传达的 涵盖某个国家/地区的涵盖作品,或您的收件人对涵盖作品的使用 在一个国家,将侵犯一项或多项可识别的专利 您有理由相信有效的国家/地区。

    如果根据单笔交易或与单笔交易有关,或 安排,您传达或通过采购运输来传播 涵盖的工作,并向某些当事方授予专利许可 接收授权他们使用、传播、修改的涵盖作品 或传达所涵盖作品的特定副本,然后获得专利许可 您的补助金将自动扩展到所涵盖的所有接受者 工作并在此基础上工作。

    如果专利许可不包括在 其覆盖范围,禁止行使或以 不行使一项或多项特定权利 根据本许可授予。如果出现以下情况,您不得转让涵盖的作品 是与第三方达成的协议的一方,该第三方在 分发软件的业务,根据该业务,您向 第三方根据您传达 工作,以及第三方授予任何一方的工作 谁会从你那里获得涵盖的作品,这是一项歧视性专利 许可 (a) 与以下机构转交的涵盖作品的副本有关 您(或由这些副本制作的副本),或 (b) 主要用于和 in 与包含 涵盖的工作,除非您签订了该安排或该专利 在2007年3月28日之前颁发了许可证。

    本许可中的任何内容均不得解释为排除或限制 任何可能涉及侵权的默示许可或其他抗辩理由 否则,根据适用的专利法,您可以使用。

  13. 不放弃他人的自由。

    如果条件强加给您(无论是通过法院命令、协议还是 否则)与本许可证的条件相矛盾,则不 请原谅您遵守本许可证的条件。如果您无法传达 涵盖的工作,以便同时履行您在 本许可和任何其他相关义务,则作为 因此,您可能根本不会传达它。例如,如果您同意 您有义务收取特许权使用费以进一步转让的条款 从您向其传达程序的人那里,这是您唯一可以的方式 同时满足这些条款,本许可将完全避免 从传达程序。

  14. 与 GNU Affero 通用公共许可证一起使用。

    尽管本许可有任何其他规定,您仍拥有 允许将任何涵盖的作品与许可的作品链接或组合 在 GNU Affero 通用公共许可证的第 3 版下转换为单个 组合工作,并传达由此产生的工作。本条款 许可证将继续适用于所涵盖作品的部分, 但是GNU Affero通用公共许可证的特殊要求, 关于通过网络进行交互的第 13 节将适用于 这样的组合。

  15. 本许可证的修订版本。

    自由软件基金会可能会发布修订版和/或新版 不时的 GNU 通用公共许可证。这样的新 版本在精神上将与当前版本相似,但可能 在细节上有所不同,以解决新的问题或疑虑。

    每个版本都有一个可区分的版本号。如果程序 指定 GNU General Public 的某个编号版本 许可证“或任何更高版本”适用于它,您可以选择 遵循该编号版本的条款和条件,或 自由软件基金会发布的任何更高版本。如果 程序没有指定 GNU General 的版本号 公共许可证,您可以选择任何由自由发布的版本 软件基础。

    如果程序指定代理可以决定将来的版本 可以使用 GNU 通用公共许可证,该代理的公共 接受版本的声明永久授权您 为程序选择该版本。

    更高版本的许可证可能会为您提供其他或不同的 权限。但是,没有对任何 作者或版权所有者,因为您选择遵循 更高版本。

  16. 免责声明。

    在允许的范围内,该程序不作任何保证 适用法律。除非另有书面说明,否则版权 持有人和/或其他方“按原样”提供程序,而无需 任何形式的明示或暗示的保证,包括但不 仅限于对适销性和适用性的默示保证 一个特定的目的。关于质量和 程序的性能与您同在。如果程序证明 有缺陷的,您承担所有必要的服务、维修或费用 校正。

  17. 责任限制。

    在任何情况下,除非适用法律要求或书面同意 任何版权所有者或任何其他修改和/或 在上述允许的情况下传达程序,对您承担损害赔偿责任, 包括任何一般、特殊、偶然或后果性损害 因使用或无法使用程序(包括 不限于数据丢失或数据不准确或 您或第三方遭受的损失或程序失败 与任何其他程序一起操作),即使此类持有人或其他 当事人已被告知此类损害的可能性。

  18. 对第15条和第16条的解释。

    如果免责声明和责任限制提供 上述内容不能根据其条款赋予当地法律效力, 复审法院应适用最接近的当地法律 绝对放弃与以下方面相关的所有民事责任 程序,除非附带保证或责任承担 程序的副本,以换取费用。

条款和条件结束

如何将这些条款应用于您的新计划

如果你开发了一个新程序,并且你希望它是最好的 可能用于公众,实现这一目标的最好方法是使它 每个人都可以在这些软件下重新分发和更改的自由软件 条款。

为此,请将以下通知附加到程序中。这是最安全的 将它们附加到每个源文件的开头,以最有效地 说明保修的排除;并且每个文件至少应具有 “版权”行和指向完整声明位置的指针。

one line to give the program's name and a brief idea of what it does.
Copyright (C) year name of author

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see https://www.gnu.org/licenses/.

此外,还要添加有关如何通过电子邮件和纸质邮件与您联系的信息。

如果程序进行终端交互,则将其输出为短 当它以交互模式启动时,请注意如下:

program Copyright (C) year name of author
This program comes with ABSOLUTELY NO WARRANTY; for details type ‘show w’.
This is free software, and you are welcome to redistribute it
under certain conditions; type ‘show c’ for details.

假设的命令 '' 和 '' 应显示 通用公共许可证的相应部分。当然,你的 程序的命令可能不同;对于 GUI 界面,您将 使用“关于框”。show wshow c

你还应该得到你的雇主(如果你是程序员)或学校, 如有,如有必要,为该程序签署“版权免责声明”。 有关这方面的更多信息,以及如何应用和遵循 GNU GPL,请参阅 https://www.gnu.org/licenses/

GNU 通用公共许可证不允许将 程序到专有程序中。如果您的程序是子例程 库,您可能会认为允许链接专有更有用 带有库的应用程序。如果这是您要执行的操作,请使用 GNU 宽通用公共许可证,而不是本许可证。但 首先,请阅读 https://www.gnu.org/licenses/why-not-lgpl.html


下一篇: , 上一篇: , 上一篇: Bison [内容][索引]

1 野牛的概念

本章介绍了许多基本概念,没有这些概念,细节 野牛是没有意义的。如果您还不知道如何使用 Bison 或 Yacc,我们建议您从仔细阅读本章开始。


下一篇: ,上一篇:野牛的概念内容】[索引]

1.1 语言和上下文无关语法

为了让 Bison 解析一种语言,它必须由上下文无关的语法来描述。这意味着您可以指定一个或多个语法分组,并给出从其构造它们的规则 部件。例如,在 C 语言中,一种分组称为 '表达式'。创建表达式的一条规则可能是,“表达式 可以由减号和另一个表达式组成“。另一个是, “表达式可以是整数”。正如你所看到的,规则往往是 递归的,但必须至少有一条规则导致 递归。

最常见的形式系统,用于呈现此类规则供人类阅读 是 Backus-Naur 形式或“BNF”,它是在 为了指定语言Algol 60。任何语法表示为 BNF 是一种与上下文无关的语法。Bison 的输入是 本质上是机器可读的 BNF。

上下文无关语法有各种重要的子类。虽然 它可以处理几乎所有与上下文无关的语法,Bison 针对什么进行了优化 称为 LR(1) 语法。简而言之,在这些语法中,它必须是可能的 了解如何仅使用单个标记解析输入字符串的任何部分 的展望。由于历史原因,Bison 默认受到 LALR(1) 的额外限制,这很难简单地解释。 有关这方面的更多信息,请参阅神秘的冲突。你可以逃跑 通过请求 IELR(1) 或规范 LR(1) 来获得这些附加限制 解析器表。请参阅 LR 工作台构造,了解如何操作。

LR(1) 语法的解析器是确定性的,这意味着 大致上,在输入中的任何一点应用的下一个语法规则是 由前面的输入和固定的有限部分唯一确定 (称为前瞻)剩余输入。与上下文无关 语法可能是模棱两可的,这意味着有多种方法可以 应用语法规则以获取相同的输入。甚至毫不含糊 语法可以是非确定性的,这意味着没有固定的 Lookahead 始终足以确定要应用的下一个语法规则。 通过适当的声明,Bison 也能够进一步解析这些内容 一般的上下文无关语法,使用一种称为 GLR 的技术 解析(对于广义 LR)。Bison 的 GLR 解析器 能够处理任何与上下文无关的语法,其 任何给定字符串的可能解析都是有限的。

在一种语言的正式语法规则中,每种句法单位 或分组由符号命名。那些通过分组构建的 根据语法规则,较小的结构称为非终端符号;那些不能细分的称为终端符号令牌种类。我们调用一段输入 对应单个终端符号、令牌和件 对应于单个非终端符号的分组

我们可以用 C 语言作为例子来说明什么符号、终端和 非终端,平均值。C 的标记是标识符、常量(数字 和字符串),以及各种关键字、算术运算符和 标点符号。因此,C 语法的终端符号包括 'identifier', 'number', 'string',加上每个关键字的一个符号, 运算符或标点符号: 'if', 'return', 'const', 'static', 'int', 'char', 'plus-sign', 'open-brace', 'close-brace', 'comma' 还有很多。 (这些标记可以细分为字符,但这是一个问题 词典编纂,而不是语法。

下面是一个简单的 C 函数,细分为令牌:

int             /* keyword ‘int’ */
square (int x)  /* identifier, open-paren, keyword ‘int’,
                   identifier, close-paren */
{               /* open-brace */
  return x * x; /* keyword ‘return’, identifier, asterisk,
                   identifier, semicolon */
}               /* close-brace */

C 的句法分组包括表达式、语句、 声明和函数定义。这些都表示在 C语言的语法由非终端符号'expression'、'statement'、 “声明”和“函数定义”。完整的语法使用了几十个 其他语言结构,每个结构都有自己的非终端符号,在 为了表达这四个含义。上面的例子是一个 功能定义;它包含一个声明和一个语句。在 语句中,每个 '' 都是一个表达式,'' 也是如此。xx * x

每个非终端符号都必须有语法规则,显示它是如何制作的 从更简单的结构中。例如,一种 C 语句是语句;这将用一个语法规则来描述,该语法规则 非正式内容如下:return

“语句”可以由“return”关键字、“expression”和 '分号'。

“语句”还有许多其他规则,每种规则都有一个 C 语句。

必须将一个非终端符号区分为特殊符号,该符号 定义语言中的完整话语。它被称为开始 符号。在编译器中,这意味着一个完整的输入程序。在 C 语言,非终端符号“定义和声明的序列” 扮演这个角色。

例如,“”是有效的 C 表达式,即 C 的有效部分 程序,但它不能作为整个 C 程序使用。在 C 的上下文无关语法,这源于“表达式”是 不是开始符号。1 + 2

Bison 解析器读取一系列标记作为其输入,并将 使用语法规则的标记。如果输入有效,则最终结果为 整个标记序列简化为一个符号为 语法的开始符号。如果我们使用 C 的语法,则整个输入 必须是“定义和声明的序列”。如果没有,则解析器 报告语法错误。


下一篇: , 上一篇: , 上一篇: 野牛的概念 [内容][索引]

1.2 从正式规则到野牛输入

形式语法是一种数学结构。定义语言 对于 Bison,您必须编写一个以 Bison 语法表达语法的文件: 一个 Bison 语法文件。请参阅 Bison 语法文件

形式语法中的非终端符号在 Bison 输入中表示 作为标识符,就像 C 中的标识符一样。按照惯例,它应该是 小写,例如 ,或 。exprstmtdeclaration

终端符号的 Bison 表示也称为令牌 善良。令牌类型也可以表示为类似 C 的标识符。由 约定,这些标识符应为大写,以区分它们 非终端:例如、 、 或 。代表 语言应以转换为大写的关键字命名。 终端符号保留用于错误恢复。 请参阅符号、终端和非终端INTEGERIDENTIFIERIFRETURNerror

终端符号也可以表示为字符文字,就像 a C 字符常量。每当令牌只是一个 单个字符(括号、加号等):在 作为该令牌的终端符号的文本。

表示终端符号的第三种方法是使用 C 字符串常量 包含多个字符。有关详细信息,请参阅符号、终端和非终端

语法规则在 Bison 语法中也有一个表达式。例如 这是 C 语句的 Bison 规则。中的分号 quotes 是一个文字字符标记,表示 C 语法的一部分 声明;裸分号和冒号是野牛标点符号 在每条规则中使用。return

stmt: RETURN expr ';' ;

请参阅语法规则


下一篇: , 上一篇: , 上一篇: 野牛的概念 [内容][索引]

1.3 语义值

形式语法仅通过标记的分类来选择标记:例如, 如果规则提到终端符号“整数常量”,则意味着任何整数常量在该位置在语法上都有效。这 常量的精确值与如何解析输入无关:如果 '' 是语法,那么 '' 或 '' 是平等的 语法。x+4x+1x+3989

但是,精确的值对于输入的含义非常重要 解析。如果编译器无法区分 4、1 和 3989 作为程序中的常量!因此,Bison 语法中的每个标记 具有标记类型和语义值。请参阅定义语言语义,了解 详。

标记类型是在语法中定义的终端符号,例如 或 。它告诉你的一切 需要知道以决定令牌可以有效显示的位置以及如何分组 它与其他代币。语法规则对标记一无所知,除了 他们的种类。INTEGERIDENTIFIER','

语义值包含有关 标记的含义,例如整数的值或 标识符。(诸如标点符号之类的标记不会 需要具有任何语义值。','

例如,输入标记可能被归类为标记类型,其语义值为 4。另一个输入令牌可能具有相同的内容 令牌种类,但值为 3989。当语法规则说这是允许的时,这些标记中的任何一个都是可以接受的,因为每个 是一个 .当解析器接受令牌时,它会跟踪 令牌的语义值。INTEGERINTEGERINTEGERINTEGER

每个分组还可以具有语义值及其非终端值 象征。例如,在计算器中,表达式通常具有 语义值,即一个数字。在编程的编译器中 语言,表达式通常具有树的语义值 结构描述表达式的含义。


下一篇: ,上一篇: ,上一篇:野牛的概念内容】[索引]

1.4 语义操作

为了有用,程序必须做的不仅仅是解析输入;它必须 还可以根据输入产生一些输出。在野牛语法中,语法 rule 可以有一个由 C 语句组成的操作。每次 解析器识别出该规则的匹配项,并执行该操作。 请参阅操作

大多数时候,操作的目的是计算语义值 从其各部分的语义值构建的整体结构。例如 假设我们有一个规则,说一个表达式可以是两个的总和 表达 式。当解析器识别出这样的总和时,每个 子表达式具有语义值,用于描述它是如何构建的。 此规则的操作应为 新识别的更大表达式。

例如,这里有一条规则说表达式可以是 两个子表达式:

expr: expr '+' expr   { $$ = $1 + $3; } ;

该操作说明如何生成 sum 表达式的语义值 从两个子表达式的值。


下一篇: , 上一篇: , 上一篇: 野牛的概念 [内容][索引]

1.5 编写 GLR 解析器

在某些语法中,野牛的确定性 LR(1) 解析算法无法决定是否应用 给定点的某些语法规则。也就是说,它可能无法 决定(根据到目前为止读取的输入)两种可能中的哪一种 减少(语法规则的应用)适用,或是否适用 减少或阅读更多输入,并在稍后的 输入。这些分别称为减少/减少冲突 (请参阅减少/减少冲突)和转移/减少冲突 (请参阅 Shift/Reduce 冲突)。

使用一个不容易修改的语法是 LR(1),一个更通用的 有时需要解析算法。如果在文件中包含 Bison 声明(请参阅 Outline of a Bison Grammar),则 结果是广义 LR (GLR) 解析器。这些解析器处理 Bison 不包含未解决冲突的语法(即,在应用之后 优先级声明)与确定性解析器相同。然而 当面临未解决的移位/减少和减少/减少冲突时,GLR 解析器使用两者兼而有之的简单权宜之计,有效地克隆 解析器来遵循这两种可能性。每个生成的解析器都可以 再次拆分,因此在任何给定时间,都可以有任意数量的可能 正在探索的解析。解析器以同步方式进行;也就是说,所有 它们消耗(移位)给定的输入符号,然后再进行 下一个。每个克隆的解析器最终都会满足两种可能的解析器之一 命运:要么遇到解析错误,在这种情况下,它只是 消失,或者它与另一个解析器合并,因为它们两个有 将输入减少为一组相同的符号。%glr-parser

在有多个解析器期间,语义操作是 已录制,但未执行。当解析器消失时,它记录下来 语义操作也会消失,并且永远不会执行。当一个 减少使两个解析器相同,导致它们合并,Bison 记录 两组语义操作。每当最后两个解析器合并时, 回到单解析器的情况,Bison 解决了所有未解决的问题 通过对所涉及的语法规则的优先级或 执行这两个操作,然后调用指定的用户定义函数 以生成任意合并结果。


1.5.1 在明确语法上使用 GLR

在最简单的情况下,您可以使用 GLR 算法 解析明确但不能成为 LR(1) 的语法。 这种语法通常需要不止一个前瞻符号。

考虑一个问题 出现在枚举类型和子范围类型的声明中 编程语言 Pascal.以下是一些示例:

type subrange = lo .. hi;
type enum = (a, b, c);

原始语言标准只允许使用数字文字和常量 子范围边界('' 和 '')的标识符,但 Extended Pascal (ISO/IEC 10206) 和许多其他 Pascal 实现允许任意 那里的表达式。这导致了以下情况,其中包含 多余的一对括号:lohi

type subrange = (a) .. b;

将此与以下枚举声明进行比较 键入只有一个值:

type enum = (a);

(这些声明是人为的,但它们在语法上是有效的,并且 更复杂的情况可能会出现在实际程序中。

这两个声明在“”标记之前看起来完全相同。跟 正常 LR(1) 单令牌前瞻,不可能在 解析标识符“”时的两种形式。然而,这是可取的 让解析器来决定这一点,因为在后一种情况下,“'”必须变成 新标识符表示枚举值,而在前一种情况下 '' 必须用其当前含义来评估,这可能是一个常数 甚至是函数调用。..aaa

您可以将“”解析为“括号中的未指定标识符”, 稍后解决,但这通常需要大量扭曲 语义动作和大部分语法,其中括号 嵌套在表达式的递归规则中。(a)

您可以考虑使用词法分析器来区分这两种形式 为当前定义和未定义的标识符返回不同的标记。 但是,如果这些声明发生在本地范围内,并且 一个外部范围,那么两种形式都是可能的——要么局部重新定义 '',或使用外部作用域中的值 ''。所以这个 方法行不通。aaa

此问题的简单解决方案是声明解析器以使用 GLR 算法。当 GLR 解析器达到临界状态时,它只是分裂 并同时遵循两个语法规则。早点或 后来,其中一个遇到解析错误。如果有“”令牌 在下一个 '' 之前,枚举类型的规则将失败,因为它 在任何地方都不能接受“”;否则,子范围类型规则将失败 因为它需要一个“”令牌。所以其中一个分支无声地失败了, 另一个继续正常,执行所有中间 在拆分期间推迟的操作。..;....

如果输入在语法上不正确,则两个分支都失败,并且解析器 像往常一样报告语法错误。

所有这一切的效果是解析器似乎“猜”到了正确的 分支来采取,或者换句话说,它似乎比 底层的 LR(1) 算法实际上允许。在此示例中,LR(2) 就足够了,但对于任何 k 来说,一些不是 LR(k) 的情况也可以这样处理。

通常,GLR 解析器可以采用二次或三次最坏情况时间,并且 对于某些人来说,当前的 Bison 解析器甚至需要指数级的时间和空间 语法。在实践中,这种情况很少发生,对于许多语法来说,确实如此 可能证明它不会发生。本示例仅包含 两个规则之间的一个冲突,以及包含 冲突不能嵌套。因此,可以存在于 任何时间都受到常数 2 的限制,解析时间仍然是线性的。

这是与上述示例相对应的 Bison 语法。它 解析 Pascal 类型声明的大幅简化形式。

%token TYPE DOTDOT ID

%left '+' '-'
%left '*' '/'
%%
type_decl: TYPE ID '=' type ';' ;

type:
  '(' id_list ')'
| expr DOTDOT expr
;
id_list:
  ID
| id_list ',' ID
;
expr:
  '(' expr ')'
| expr '+' expr
| expr '-' expr
| expr '*' expr
| expr '/' expr
| ID
;

当用作正常的 LR(1) 语法时,Bison 会正确地抱怨 关于一个减少/减少冲突。在冲突的情况下, 解析器选择其中一个备选方案,任意选择一个 首先声明。因此,以下正确输入不是 认可:

type t = (a) .. b;

解析器可以变成 GLR 解析器,同时还可以告诉 Bison 要对一个已知的减少/减少冲突保持沉默,请添加 这两个声明对 Bison 语法文件(在第一个之前) ‘’):%%

%glr-parser
%expect-rr 1

不需要改变语法本身。现在,解析器可以识别所有 根据上述有限的语法,有效的声明是透明的。 事实上,用户甚至没有注意到解析器何时分裂。

因此,这里我们有一个案例,我们可以利用 GLR 的优势,几乎没有 弊。然而,即使在像这样的简单情况下,至少也有 需要注意两个潜在的问题。首先,始终分析冲突 由 Bison 报告,以确保 GLR 拆分仅在原地完成 打算。无意中拆分 GLR 解析器可能会导致较少的问题 比 LR 解析器静态选择错误的替代方案更明显 冲突。其次,要非常谨慎地考虑与词法分析器的交互(参见 Token Kinds 中的语义信息)。由于拆分解析器使用没有 在拆分过程中执行任何操作,词法分析器都无法获得信息 通过解析器操作。词法分析器相互作用的一些情况可以通过以下方式消除 使用 GLR 将复杂功能从词法分析器转移到解析器。你必须 检查其余案例的正确性。

在我们的示例中,词法分析器可以安全地返回基于以下 它们在某个符号表中的当前含义,因为没有新符号 在类型声明的中间定义。虽然有可能 解析器,用于在解析枚举常量时定义枚举常量,在 类型声明完成,实际上没有区别,因为它们 不能在同一枚举类型声明中使用。


下一篇: ,上一篇: ,上一篇: 编写 GLR 解析器 [内容][索引]

1.5.2 使用 GLR 解决歧义

让我们考虑一个示例,从 C++ 大大简化 语法。1

%{
  #include <stdio.h>
  int yylex (void);
  void yyerror (char const *);
%}

%define api.value.type {char const *}

%token TYPENAME ID

%right '='
%left '+'

%glr-parser

%%

prog:
  %empty
| prog stmt   { printf ("\n"); }
;

stmt:
  expr ';'  %dprec 1
| decl      %dprec 2
;

expr:
  ID               { printf ("%s ", $$); }
| TYPENAME '(' expr ')'
                   { printf ("%s <cast> ", $1); }
| expr '+' expr    { printf ("+ "); }
| expr '=' expr    { printf ("= "); }
;

decl:
  TYPENAME declarator ';'
                   { printf ("%s <declare> ", $1); }
| TYPENAME declarator '=' expr ';'
                   { printf ("%s <init-declare> ", $1); }
;

declarator:
  ID               { printf ("\"%s\" ", $1); }
| '(' declarator ')'
;

这模拟了 C++ 语法的一个有问题的部分——之间的歧义 某些声明和声明。例如

T (x) = y+z;

解析为 an 或 a(假设 '' 被识别为 a 和 '' 作为 )。 Bison 将此检测为规则 和 之间的 reduce/reduce 冲突,它无法在 在上面的示例中遇到的时间。由于这是一个 GLR 解析器,因此它将问题拆分为两个解析,一个用于 解决 reduce/reduce 冲突的每个选择。 与上一节中的示例不同(请参阅在明确语法上使用 GLR), 然而,这些解析都没有“死亡”,因为它的语法是 模糊。其中一个解析器最终会减少和 另一个 reduce ,之后两个解析器都处于 相同的状态:他们看到了“”,并且具有相同的未处理状态 剩余输入。我们说这些解析已经合并。exprstmtTTYPENAMExIDexpr : IDdeclarator : IDxstmt : expr ';'stmt : declprog stmt

此时,GLR 解析器需要 如何在竞争解析之间进行选择的语法。 在上面的示例中,两个声明指定 Bison 优先 到将示例解释为 的解析,这意味着 that 是一个声明器。 因此,解析器打印%dprecdeclx

"x" y z + T <init-declare>

只有当多个声明时,这些声明才会发挥作用 解析幸存下来。请考虑此解析器的不同输入字符串:%dprec

T (x) + y;

这是使用 GLR 解析明确 构造,如上一节所示(请参阅在明确语法上使用 GLR)。 在这里,没有歧义(这不能解析为声明)。 但是,当 Bison 解析器遇到 时,它没有 有足够的信息来解决减少/减少冲突(再次, 之间作为 an 或 a )。在这个 case,则不使用优先级声明。同样,解析器分裂 一分为二,一个假设是 ,另一个假设是 假设是 .这些解析器中的第二个 然后在看到 时消失,解析器打印xxexprdeclaratorxexprxdeclarator+

x T <cast> y +

假设您不想解决歧义,而是想看到所有内容 可能性。为此,必须合并语义 两个可能的解析器的操作,而不是选择一个而不是 其他。为此,您可以更改 遵循:stmt

stmt:
  expr ';'  %merge <stmt_merge>
| decl      %merge <stmt_merge>
;

并将函数定义为:stmt_merge

static YYSTYPE
stmt_merge (YYSTYPE x0, YYSTYPE x1)
{
  printf ("<OR> ");
  return "";
}

并附有远期声明 在文件开头的 C 声明中:

%{
  static YYSTYPE stmt_merge (YYSTYPE x0, YYSTYPE x1);
%}

使用这些声明,生成的分析器将分析第一个示例 作为 an 和 a ,并打印exprdecl

"x" y z + T <init-declare> x T <cast> y z + = <OR>

Bison 要求所有 参与任何特定合并的作品具有相同的 '' 子句。否则,歧义将无法解决, 解析器将在任何解析期间报告错误,导致 有问题的合并。%merge


合并的签名取决于符号的类型。在 前面的示例中,合并到符号 () 没有 特定类型,合并为stmt

YYSTYPE stmt_merge (YYSTYPE x0, YYSTYPE x1);

但是,如果具有声明的类型,例如,stmt

%type <Node *> stmt;

%union {
  Node *node;
  ...
};
%type <node> stmt;

那么合并的原型必须是:

Node *stmt_merge (YYSTYPE x0, YYSTYPE x1);

(这个签名最初可能是一个错误,也许它应该是一个错误 ‘’.如果您对以下内容有意见 它,请告诉我们。Node *stmt_merge (Node *x0, Node *x1)


下一篇: , 上一篇: , 上一篇: 编写 GLR 解析器 [Contents][索引]

1.5.3 GLR语义操作

GLR 解析的性质和生成的结构 解析器对语义值和操作产生了某些限制。

1.5.3.1 延迟语义操作

根据定义,延迟语义操作不会同时执行 相关的减少。 这引发了您可能在语义中使用的几个 Bison 特征的警告 GLR 解析器中的操作。

在任何语义操作中,您都可以检查以确定类型 在相关减少时存在的 Lookahead 令牌。 检查未设置为 或 后,可以检查 和 确定 Lookahead 令牌的语义值和位置(如果有)。在 非延迟语义操作,您还可以将这些变量中的任何一个修改为 影响句法分析。请参阅 Lookahead 令牌yycharyycharYYEMPTYYYEOFyylvalyylloc

在延迟语义操作中,影响语法分析为时已晚。 在本例中,、 和 设置为 它们在相关减少时所拥有的值的浅拷贝。 仅出于这个原因,修改它们就很危险。 此外,修改它们的结果是未定义的,并且可能会随着 野牛的未来版本。 例如,如果语义操作可能被推迟,则永远不应该编写它 调用(请参阅在操作中使用的特殊功能)或尝试释放 引用的内存。yycharyylvalyyllocyyclearinyylval

1.5.3.2 YY错误

另一个需要特别考虑的 Bison 功能是(请参阅在操作中使用的特殊功能),您可以在语义操作中调用它来 启动错误恢复。 在确定性 GLR 操作期间,效果为 与它在确定性解析器中的效果相同。 推迟行动中的效果是相似的,但 error 未定义;相反,解析器将恢复为确定性操作, 选择要继续处理语法错误的未指定堆栈。 在语义谓词中(请参阅使用任意谓词控制解析)期间 解析,默默修剪 调用测试的分析。YYERRORYYERRORYYERROR

1.5.3.3 语义值和位置的限制

GLR 解析器要求您使用 POD(纯旧数据)类型 使用生成的解析器时的语义值和位置类型 C++ 代码。


上一篇: , 上一篇: 编写 GLR 解析器 [内容][索引]

1.5.4 使用任意谓词控制解析

除了 和 指令之外, GLR 解析器 允许您根据执行的任意计算拒绝解析 在用户代码中,而没有让 Bison 将此拒绝视为错误 如果有替代解析。例如%dprec%merge

widget:
  %?{  new_syntax } "widget" id new_args  { $$ = f($3, $4); }
| %?{ !new_syntax } "widget" id old_args  { $$ = f($3, $4); }
;

是允许同一解析器处理两种不同语法的一种方法 部件。前面的从句被视为普通条款 Midrule 操作,除了其文本作为表达式处理并且始终 立即评估(即使在非确定性模式下)。如果 表达式产生 0 (false),该子句被视为语法错误, 在非确定性解析器中,这会导致减少它的堆栈 去死。在确定性解析器中,它的行为类似于 .%?YYERROR

如示例所示,谓词在其他方面看起来像语义操作,并且 因此,在确定数字时必须考虑它们 用于表示右侧符号的语义值。 但是,谓词动作没有定义的值,并且可能不会给出 标签。

语义谓词和普通谓词之间存在细微的区别 非确定性模式下的操作,因为后者是延迟的。 例如,我们可以尝试将前面的示例重写为

widget:
  { if (!new_syntax) YYERROR; }
    "widget" id new_args  { $$ = f($3, $4); }
|  { if (new_syntax) YYERROR; }
    "widget" id old_args  { $$ = f($3, $4); }
;

(颠倒谓词测试的意义,当它们 false)。但是,这 如果 和 具有重叠的语法,则具有相同的效果。 由于 midrule 操作测试被推迟, GLR 解析器首先遇到未解决的模棱两可的约简 对于在执行 的测试之前识别同一字符串的情况。因此,它 报告错误。new_argsold_argsnew_syntaxnew_argsold_argsnew_syntax

最后,在写谓词时要小心:推迟的操作没有 assessd,因此在谓词中使用它们将产生未定义的效果。


1.6 地点

许多应用程序(如解释器或编译器)必须产生冗长的内容 和有用的错误消息。为此,必须能够跟踪 每个句法结构的文本位置位置。 Bison 提供了一种处理这些位置的机制。

每个标记都有一个语义值。以类似的方式,每个令牌都有一个 关联的位置,但所有令牌的位置类型相同 和分组。此外,输出解析器配备了默认数据 用于存储位置的结构(有关详细信息,请参阅跟踪位置 详细信息)。

与语义值一样,可以使用专用的 构造集。在上面的示例中,整个分组的位置 是 ,而子表达式的位置是 和 。@$@1@3

匹配规则时,将使用默认操作来计算语义值 (请参阅操作)。以同样的方式,另一个默认值 操作用于位置。但是,针对位置的操作是一般性的 对于大多数情况来说已经足够了,这意味着通常不需要对每个情况进行描述 规则应该如何形成。为给定位置构建新位置时 分组时,输出解析器的默认行为是取开头 第一个符号和最后一个符号的末尾。@$


下一篇: , 上一篇: , 上一篇: 野牛的概念 [内容][索引]

1.7 Bison 输出:解析器实现文件

当你运行 Bison 时,你给它一个 Bison 语法文件作为输入。这 最重要的输出是一个 C 源文件,它实现了 语法描述的语言。此解析器称为 Bison 解析器,此文件称为 Bison 解析器 实现文件。请记住,Bison 实用程序和 Bison 解析器是两个不同的程序:Bison 实用程序是一个程序 其输出是成为一部分的 Bison 解析器实现文件 你的程序。

Bison 解析器的工作是根据 语法规则 - 例如,将标识符和运算符构建到 表达 式。当它这样做时,它会运行语法规则的操作 使用。

标记来自一个名为词法分析器的函数,该函数 您必须以某种方式提供(例如用 C 编写它)。野牛 分析器每次需要新令牌时都会调用词法分析器。它 不知道令牌的“内部”是什么(尽管它们的语义值 可能反映了这一点)。通常,词法分析器通过以下方式制作标记 解析文本字符,但 Bison 不依赖于此。 请参阅 Lexical Analyzer 函数 yylex

Bison 解析器实现文件是 C 代码,它定义了 函数命名,用于实现该语法。这 函数不能制作一个完整的 C 程序:您必须提供一些 附加功能。一个是词汇分析器。另一个是 错误报告函数,解析器调用该函数来报告错误。 此外,一个完整的 C 程序必须从一个名为 ;您必须提供此信息,并安排它调用,否则解析器将永远无法运行。请参阅分析器 C 语言接口yyparsemainyyparse

除了令牌种类名称和操作中的符号外,您 write,Bison解析器实现文件中定义的所有符号 本身以 '' 或 '' 开头。这包括接口 函数,例如词法分析器函数、 错误报告函数和解析器函数本身。这还包括使用的众多标识符 用于内部目的。因此,您应该避免使用 C Bison 语法中以“”或“”开头的标识符 文件,但本手册中定义的文件除外。另外,你应该 避免使用 C 标识符 '' 和 '' 除了通常的含义之外的任何内容。yyYYyylexyyerroryyparseyyYYmallocfree

在某些情况下,Bison 解析器实现文件包括 system 标头,在这些情况下,您的代码应遵循标识符 由这些标头保留。在某些非 GNU 主机上,包含 、 、 (如果可用) 和 来声明内存分配器和整数类型和常量。 如果正在使用消息翻译,则包括在内 (请参阅解析器国际化)。可能包括其他系统标头 如果将(请参阅跟踪解析器)或(请参阅野牛符号)定义为非零值。<limits.h><stddef.h><stdint.h><stdlib.h><libintl.h>YYDEBUGYYSTACK_USE_ALLOCA


1.8 使用野牛的阶段

使用 Bison 的实际语言设计过程,来自语法规范 对于工作编译器或解释器,具有以下部分:

  1. 以 Bison 识别的形式正式指定语法 (参见 Bison 语法文件)。对于每个语法规则 用语言描述当 该规则的实例被识别。该操作由 C 语句的序列。
  2. 编写词法分析器来处理输入并将标记传递给解析器。 词法分析器可以用 C 语言手动编写(参见词法分析器函数 yylex)。它 也可以使用 Lex 生成,但 Lex 的使用未在 本手册。
  3. 编写一个控制函数来调用 Bison 生成的解析器。
  4. 编写错误报告例程。

要将编写的源代码转换为可运行的程序,您需要 必须按照以下步骤操作:

  1. 在语法上运行 Bison 以生成解析器。
  2. 编译 Bison 输出的代码以及任何其他源文件。
  3. 链接目标文件以生成成品。

上一篇: ,上一篇:野牛的概念 [内容][索引]

1.9 野牛语法的整体布局

Bison 实用程序的输入文件是 Bison 语法文件。这 Bison 语法文件的一般形式如下:

%{
Prologue
%}

Bison declarations

%%
Grammar rules
%%
Epilogue

“'”、“'”和“”是出现的标点符号 在每个 Bison 语法文件中分隔各部分。%%%{%}

序言可以定义操作中使用的类型和变量。您可以 此外,使用预处理器命令来定义此处使用的宏,并用于包含执行任何这些操作的头文件。 您需要声明词法分析器和错误 打印机,以及任何其他全局标识符 由语法规则中的操作使用。#includeyylexyyerror

Bison 声明声明终端和非终端的名称 符号,还可以描述运算符优先级和 各种符号的语义值。

语法规则定义了如何从其构造每个非终端符号 部件。

结语可以包含要使用的任何代码。通常 序言中声明的函数定义请点击此处。在 简单的程序,程序的其余部分都可以转到这里。


下一篇: , 上一篇: , 上一篇: 野牛 [内容][索引]

2 示例

现在我们展示并解释几个使用 Bison 编写的示例程序: 反向波兰符号计算器,一种代数(中缀)符号 计算器 — 后来扩展到跟踪“位置” — 以及多功能计算器。都 生产可用但有限的交互式桌面计算器。

这些例子很简单,但实际编程的 Bison 语法 语言的编写方式相同。您可以将这些示例复制到 源文件来尝试它们。


Bison 附带了几个示例(包括针对不同目标的示例) 语言)。如果此软件包安装正确,您将在 中找到它们,根目录在哪里 的安装,可能类似于 或 .prefix/share/doc/bison/examplesprefix/usr/local/usr


2.1 反向波兰符号计算器

第一个示例2 是简单的双精度反向 波兰语 符号计算器(使用后缀运算符的计算器)。此示例 提供了一个很好的起点,因为运算符优先级不是问题。 第二个示例将说明如何处理运算符优先级。

此计算器的源代码名为 。这 '' 扩展名是用于 Bison 语法文件的约定。rpcalc.y.y


2.1.1 声明rpcalc

以下是反向波兰符号的 C 和 Bison 声明 计算器。与 C 语言一样,注释位于 '' 或 在“”之后。/*…*///

/* Reverse Polish Notation calculator. */

%{
  #include <stdio.h>
  #include <math.h>
  int yylex (void);
  void yyerror (char const *);
%}
%define api.value.type {double}
%token NUM

%% /* Grammar rules and actions follow. */

声明部分(见序言)包含两个 预处理器指令和两个前向声明。

该指令用于声明幂 功能。#includepow

和 的转发声明是 需要,因为 C 语言要求声明函数 在使用之前。这些函数将在 结语,但解析器调用它们,因此必须在 序幕。yylexyyerror

第二部分,野牛声明,向野牛提供关于 令牌及其类型(参见 Bison 声明部分)。

该指令定义变量 , 从而为标记和 分组(请参阅语义值的数据类型)。野牛 解析器将使用定义为的任何类型;如果你 不要定义它,是默认设置。因为我们指定 '',每个标记和每个表达式都有一个关联的值, 这是一个浮点数。C代码可以用来指代 值 。%defineapi.value.typeapi.value.typeint{double}YYSTYPEapi.value.type

每个不是单字符文字的终端符号都必须是 宣布。(单字符文字通常不需要声明。 在此示例中,所有算术运算符都由 单字符文字,因此唯一需要的终端符号 声明为 ,数值常量的标记类型。NUM


下一篇: , 上一篇: , 上一篇: 反向波兰符号计算器 [内容][索引]

2.1.2 语法规则rpcalc

以下是反向波兰语符号计算器的语法规则。

input:
  %empty
| input line
;
line:
  '\n'
| exp '\n'      { printf ("%.10g\n", $1); }
;
exp:
  NUM
| exp exp '+'   { $$ = $1 + $2;      }
| exp exp '-'   { $$ = $1 - $2;      }
| exp exp '*'   { $$ = $1 * $2;      }
| exp exp '/'   { $$ = $1 / $2;      }
| exp exp '^'   { $$ = pow ($1, $2); }  /* Exponentiation */
| exp 'n'       { $$ = -$1;          }  /* Unary minus   */
;
%%

此处定义的 rpcalc“语言”的分组是表达式 (给定名称)、输入行 () 和 完整的输入成绩单 ()。这些非终端中的每一个 符号有几个替代规则,由竖条“”连接 读作“或”。以下各节介绍了这些规则的内容 意味 着。explineinput|

语言的语义是由当 识别分组。这些操作是出现在里面的 C 代码 括号。请参阅操作

您必须在 C 中指定这些操作,但 Bison 提供了 在规则之间传递语义值。在每个操作中, 伪变量代表分组的语义值 规则将要构建。赋值 是 大多数动作的主要工作。组件的语义值 规则称为 、 等。$$$$$1$2


下一篇: , 上一篇: rpcalc 的语法规则 [内容][索引]

2.1.2.1 解释input

考虑以下定义:input

input:
  %empty
| input line
;

该定义如下:“完整的输入要么是空的 字符串,或一个完整的输入后跟一行输入“。请注意 “完整输入”是根据其本身来定义的。这个定义是说的 要保持递归,因为总是显示为 序列中最左边的符号。请参阅递归规则input

第一个备选方案为空,因为 冒号和第一个“”;这意味着可以匹配 输入的空字符串(无标记)。我们以这种方式编写规则,因为它 在启动计算器后立即键入是合法的。 按照惯例,首先放置一个空的替代方案,然后使用 (可选)指令,或在其中写上注释 ''(参见空规则)。|inputCtrl-d%empty/* empty */

第二个备用规则 () 处理所有非平凡的输入。 它的意思是,“在阅读任意数量的行后,如果 可能。左递归使此规则形成一个循环。由于 第一个备选方案匹配空输入,循环可以执行零或 更多次。input line

解析器函数继续处理输入,直到 出现语法错误或词汇分析器说没有更多 输入令牌;我们将安排后者在输入结束时发生。yyparse


下一篇: , 上一篇: , 上一篇: rpcalc 语法规则 [内容][索引]

2.1.2.2 解释line

现在考虑以下定义:line

line:
  '\n'
| exp '\n'  { printf ("%.10g\n", $1); }
;

第一种替代方法是换行符的标记;这意味着 该 RPCALC 接受一个空行(并忽略它,因为没有 行动)。第二种选择是表达式后跟换行符。 这是使 rpcalc 有用的替代方案。的语义值 分组是 的值,因为 问题 是备选方案中的第一个符号。该操作将打印此内容 value,这是用户请求的计算结果。exp$1exp

此操作是不寻常的,因为它不会将值赋给 。如 结果,与 是 关联的语义值 未初始化(其值将不可预测)。如果出现以下情况,这将是一个错误 该值曾经被使用过,但我们不使用它:一旦 RPCALC 打印了 值,则不再需要该值。$$line


上一篇: , 上一篇: rpcalc 语法规则 [内容][索引]

2.1.2.3 解释exp

分组有多个规则,每种表达式一个规则。 第一条规则处理最简单的表达式:那些只是 数字。第二个处理一个加法表达式,它看起来像两个 表达式后跟加号。第三个处理减法,等等 上。exp

exp:
  NUM
| exp exp '+'     { $$ = $1 + $2;    }
| exp exp '-'     { $$ = $1 - $2;    }
…
;

我们使用 '' 来连接 的所有规则,但我们可以 同样,将它们分开编写:|exp

exp: NUM;
exp: exp exp '+'  { $$ = $1 + $2; };
exp: exp exp '-'  { $$ = $1 - $2; };
…

大多数规则都具有计算表达式值的操作 其部分价值的条款。例如,在加法规则中,引用第一个组件并引用 第二个。第三个组件 ,没有意义 关联的语义值,但如果它有语义值,您可以将其称为 .第一条规则依赖于隐式默认操作:“'”。$1exp$2'+'$3{ $$ = $1; }

当使用此规则识别求和表达式时,总和 两个子表达式的值作为整体的值生成 表达。请参阅操作yyparse

您不必为每条规则都执行操作。当规则没有操作时, 默认情况下,Bison 将 的值复制到 。这就是 发生在第一条规则(使用 的那条规则)中。$1$$NUM

此处显示的格式是推荐的约定,但 Bison 不是 需要它。您可以根据需要添加或更改空格。为 例如:

exp: NUM | exp exp '+' {$$ = $1 + $2; } | … ;

含义与此相同:

exp:
  NUM
| exp exp '+'    { $$ = $1 + $2; }
| …
;

然而,后者更具可读性。


下一篇: , 上一篇: , 上一篇: 反向波兰符号计算器 [内容][索引]

2.1.3 词汇分析器rpcalc

词汇分析器的工作是低级解析:转换字符 或将字符序列转换为标记。Bison 解析器得到它的 通过调用词法分析器来标记。请参阅 Lexical Analyzer 函数 yylex

RPN 只需要一个简单的词法分析器 计算器。这 词法分析器跳过空格和制表符,然后读取数字 as 并将它们作为标记返回。任何其他字符 那不是数字的一部分,而是一个单独的令牌。请注意,令牌代码 对于这样的单字符标记就是字符本身。doubleNUM

词法分析器函数的返回值是一个数字代码,它 表示令牌类型。野牛规则中使用的相同文本代表 此标记类型也是该类型的数字代码的 C 表达式。 这以两种方式工作。如果令牌类型是字符文字,则其 数字代码是字符的数字代码;您可以使用相同的字符 字面上表示数字。如果令牌种类是 一个标识符,该标识符由 Bison 定义为 C 枚举,其 definition 是适当的代码。因此,在此示例中,成为要使用的枚举。NUMyylex

令牌的语义值(如果有)存储在全局 变量 ,这是 Bison 解析器将查找它的位置。 (的 C 数据类型是 ,其值已定义 在语法的开头通过“”;请参阅 rpcalc 的声明yylvalyylvalYYSTYPE%define api.value.type {double}

如果遇到输入结束,则返回令牌种类代码 0。 (Bison 将任何非正值识别为表示输入结束。

下面是词法分析器的代码:

/* The lexical analyzer returns a double floating point
   number on the stack and the token NUM, or the numeric code
   of the character read if not a number.  It skips all blanks
   and tabs, and returns 0 for end-of-input. */

#include <ctype.h>
#include <stdlib.h>
int
yylex (void)
{
  int c = getchar ();
  /* Skip white space. */
  while (c == ' ' || c == '\t')
    c = getchar ();
  /* Process numbers. */
  if (c == '.' || isdigit (c))
    {
      ungetc (c, stdin);
      if (scanf ("%lf", &yylval) != 1)
        abort ();
      return NUM;
    }
  /* Return end-of-input. */
  else if (c == EOF)
    return YYEOF;
  /* Return a single char. */
  else
    return c;
}

2.1.4 控制功能

根据这个例子的精神,控制功能是 保持在最低限度。唯一的要求是它调用以启动解析过程。yyparse

int
main (void)
{
  return yyparse ();
}

下一篇: , 上一篇: , 上一篇: 反向波兰符号计算器 [内容][索引]

2.1.5 错误报告例程

当检测到语法错误时,它会调用错误报告 函数打印错误消息(通常但不是 总是 )。由程序员提供(参见解析器 C 语言接口),所以 以下是我们将使用的定义:yyparseyyerror"syntax error"yyerror

#include <stdio.h>

/* Called by yyparse on error. */
void
yyerror (char const *s)
{
  fprintf (stderr, "%s\n", s);
}

返回后,Bison 解析器可能会从错误中恢复 如果语法包含合适的错误规则,则继续解析 (请参阅错误恢复)。否则,返回非零。我们 在此示例中没有编写任何错误规则,因此任何无效的输入都会 导致计算器程序退出。这不是干净的行为 真正的计算器,但对于第一个例子来说已经足够了。yyerroryyparse


下一篇: , 上一篇: , 上一篇: 反向波兰符号计算器 [Contents][Index]

2.1.6 运行 Bison 来制作解析器

在运行 Bison 生成解析器之前,我们需要决定如何 将所有源代码排列在一个或多个源文件中。对于这样的 简单的例子,最简单的事情就是把所有东西都放在一个文件中, 语法文件。和 的定义在语法文件的尾声中 (参见 The Overall Layout of a Bison Grammar)。yylexyyerrormain

对于一个大型项目,您可能有多个源文件,并用于安排重新编译它们。make

对于语法文件中的所有源代码,您可以使用以下命令 要将其转换为解析器实现文件,请执行以下操作:

$ bison file.y

在此示例中,语法文件被调用 (for “反向波兰 CALCulator”)。Bison 生成解析器 实现文件,删除了 '' 从语法文件名中。分析器实现文件 包含 的源代码。附加功能 在语法文件中 (, 和 ) 是 逐字复制到解析器实现文件。rpcalc.yfile.tab.c.yyyparseyylexyyerrormain


2.1.7 编译解析器实现文件

以下是编译和运行解析器实现文件的方法:

# List files in current directory.
$ ls
rpcalc.tab.c  rpcalc.y
# Compile the Bison parser.
# -lm tells compiler to search math library for pow.
$ cc -lm -o rpcalc rpcalc.tab.c
# List files again.
$ ls
rpcalc  rpcalc.tab.c  rpcalc.y

该文件现在包含可执行代码。这是一个 使用 的示例会话。rpcalcrpcalc

$ rpcalc
4 9 +
⇒ 13
3 7 + 3 4 5 *+-
⇒ -13
3 7 + 3 4 5 * + - n              Note the unary minus, ‘n
⇒ 13
5 6 / 4 n +
⇒ -3.166666667
3 4 ^                            Exponentiation
⇒ 81
^D                               End-of-file indicator
$

下一篇: , 上一篇: , 上一篇: 示例 [内容][索引]

2.2 中缀符号计算器:calc

我们现在修改 rpcalc 来处理中缀运算符,而不是 后缀。3 中缀 表示法涉及运算符优先级的概念和 嵌套到任意深度的括号。这是 的 Bison 代码,一个中缀桌面计算器。calc.y

/* Infix notation calculator. */

%{
  #include <math.h>
  #include <stdio.h>
  int yylex (void);
  void yyerror (char const *);
%}
/* Bison declarations. */
%define api.value.type {double}
%token NUM
%left '-' '+'
%left '*' '/'
%precedence NEG   /* negation--unary minus */
%right '^'        /* exponentiation */
%% /* The grammar follows. */
input:
  %empty
| input line
;
line:
  '\n'
| exp '\n'  { printf ("\t%.10g\n", $1); }
;
exp:
  NUM
| exp '+' exp        { $$ = $1 + $3;      }
| exp '-' exp        { $$ = $1 - $3;      }
| exp '*' exp        { $$ = $1 * $3;      }
| exp '/' exp        { $$ = $1 / $3;      }
| '-' exp  %prec NEG { $$ = -$2;          }
| exp '^' exp        { $$ = pow ($1, $3); }
| '(' exp ')'        { $$ = $2;           }
;
%%

函数 ,可以是 和以前一样。yylexyyerrormain

此代码中显示了两个重要的新功能。

在第二部分(Bison 声明)中,声明令牌 种类,并说它们是左关联运算符。声明 和 (右关联性) 取代了用于声明令牌种类名称而不 关联性/优先级。(这些标记是单字符文字, 通常不需要声明。我们在这里声明它们是为了指定 关联性/优先级。%left%left%right%token

运算符优先级由 声明;声明的行号越大(越低 页面或屏幕),优先级越高。因此,幂 优先级最高,一元减号()紧随其后 通过 '' 和 '',依此类推。一元减号不具有关联性, 只有优先级很重要(.请参阅运算符优先级NEG*/%precedence

另一个重要的新功能是语法 部分。简单的指示 Bison 认为规则 '' 具有相同的优先级,在本例中为 次高。请参阅上下文相关优先级%prec%prec| '-' expNEG

下面是一个示例运行:calc.y

$ calc
4 + 4.5 - (34/(8*3+-3))
6.880952381
-56 + 2
-54
3 ^ 2
9

下一篇: , 上一篇: , 上一篇: 示例 [内容][索引]

2.3 简单的错误恢复

到目前为止,本手册尚未解决错误问题 恢复 - 如何在解析器检测到语法后继续解析 错误。我们处理的只是错误报告。 回想一下,默认情况下,调用 后返回 。这意味着错误的输入行会导致 计算器程序退出。现在我们展示如何纠正这一缺陷。yyerroryyparseyyerror

野牛语言本身包括保留词,它 可以包含在语法规则中。在下面的示例中,它有 已添加到以下替代项之一:errorline

line:
  '\n'
| exp '\n'   { printf ("\t%.10g\n", $1); }
| error '\n' { yyerrok;                  }
;

语法的这一补充允许在 语法错误事件。如果无法计算的表达式是 读取,则该错误将被第三条规则识别为 , 解析将继续。(该函数仍被调用 也打印其消息。该操作执行语句,一个由Bison自动定义的宏;它的意思是 错误恢复已完成(请参阅错误恢复)。请注意 和 之间的区别 ;两者都不是 错字。lineyyerroryyerrokyyerrokyyerror

这种形式的错误恢复处理语法错误。还有其他 各种错误;例如,除以零,这会引发异常 通常致命的信号。真正的计算器程序必须处理这个问题 信号和使用返回和恢复解析 输入线;它还必须丢弃当前行的其余部分 输入。我们不会进一步讨论这个问题,因为它不是特定于 野牛程序。longjmpmain


下一篇: , 上一篇: , 上一篇: 示例 [内容][索引]

2.4 位置跟踪计算器:ltcalc

本示例使用位置扩展中缀表示法计算器 跟踪。此功能将用于改进错误消息。为 为了清楚起见,这个例子是一个简单的整数计算器,因为 使用位置所需的大部分工作将在词典中完成 分析器。


2.4.1 声明ltcalc

位置跟踪计算器的 C 和 Bison 声明是 与中缀表示法计算器的声明相同。

/* Location tracking calculator. */

%{
  #include <math.h>
  int yylex (void);
  void yyerror (char const *);
%}

/* Bison declarations. */
%define api.value.type {int}
%token NUM

%left '-' '+'
%left '*' '/'
%precedence NEG
%right '^'

%% /* The grammar follows. */

请注意,没有特定于位置的声明。定义数据类型 对于不需要存储位置:我们将使用 默认值(请参阅位置的数据类型),这是一个四成员结构,具有 以下整数字段:、 和 。按照惯例,并按照 根据 GNU 编码标准和惯例,行数和列数 两者都从 1 开始。first_linefirst_columnlast_linelast_column


下一篇: , 上一篇: , 上一篇: 位置跟踪计算器: ltcalc [内容][索引]

2.4.2 语法规则ltcalc

是否处理位置对 your 的语法没有影响 语言。因此,此示例的语法规则将非常接近 对于上一个示例中的那些:我们只会修改它们以使其受益 从新信息。

在这里,我们将使用位置来报告除以零,并找到 错误的表达式或子表达式。

input:
  %empty
| input line
;
line:
  '\n'
| exp '\n' { printf ("%d\n", $1); }
;
exp:
  NUM
| exp '+' exp   { $$ = $1 + $3; }
| exp '-' exp   { $$ = $1 - $3; }
| exp '*' exp   { $$ = $1 * $3; }
| exp '/' exp
    {
      if ($3)
        $$ = $1 / $3;
      else
        {
          $$ = 1;
          fprintf (stderr, "%d.%d-%d.%d: division by zero",
                   @3.first_line, @3.first_column,
                   @3.last_line, @3.last_column);
        }
    }
| '-' exp %prec NEG     { $$ = -$2; }
| exp '^' exp           { $$ = pow ($1, $3); }
| '(' exp ')'           { $$ = $2; }

此代码演示如何通过以下方式到达语义操作中的位置 使用规则组件的伪变量,以及 分组的伪变量。@n@$

我们不需要为:输出解析器赋值 自然而然。默认情况下,在执行每个操作的 C 代码之前,对于包含组件的规则,设置为从 的开头到结尾的范围。此行为可以是 重新定义(请参阅位置的默认操作),对于非常具体的规则,可以手动计算。@$@$@1@nn@$


2.4.3 词汇分析器。ltcalc

到目前为止,我们依靠 Bison 的默认设置来启用位置 跟踪。下一步是重写词法分析器,并使其 能够向解析器提供令牌位置,就像它已经为 语义值。

为此,我们必须考虑到 输入文本,以避免计算位置模糊或错误:

int
yylex (void)
{
  int c;
  /* Skip white space. */
  while ((c = getchar ()) == ' ' || c == '\t')
    ++yylloc.last_column;
  /* Step. */
  yylloc.first_line = yylloc.last_line;
  yylloc.first_column = yylloc.last_column;
  /* Process numbers. */
  if (isdigit (c))
    {
      yylval = c - '0';
      ++yylloc.last_column;
      while (isdigit (c = getchar ()))
        {
          ++yylloc.last_column;
          yylval = yylval * 10 + c - '0';
        }
      ungetc (c, stdin);
      return NUM;
    }
  /* Return end-of-input. */
  if (c == EOF)
    return YYEOF;

  /* Return a single char, and update location. */
  if (c == '\n')
    {
      ++yylloc.last_line;
      yylloc.last_column = 0;
    }
  else
    ++yylloc.last_column;
  return c;
}

基本上,词法分析器执行与以前相同的处理:它 跳过空格和制表符,并读取数字或单字符标记。在 此外,它还会更新包含令牌位置的全局变量(类型)。yyllocYYLTYPE

现在,每次此函数返回令牌时,解析器的类型为 以及它的语义价值,以及它在文本中的位置。最后需要的 更改是初始化,例如在控制 功能:yylloc

int
main (void)
{
  yylloc.first_line = yylloc.last_line = 1;
  yylloc.first_column = yylloc.last_column = 0;
  return yyparse ();
}

请记住,计算位置不是语法问题。每 字符必须与位置更新相关联,无论它是否在 有效输入、注释、文本字符串等。


下一篇: , 上一篇: , 上一篇: 示例 [内容][索引]

2.5 多功能计算器:mfcalc

既然已经讨论了 Bison 的基础知识,现在是时候继续讨论 更高级的问题。4 以上计算器仅提供 五个功能,''、''、''、''和''。它 如果有一个提供其他数学的计算器就好了 函数,如 、 等。+-*/^sincos

很容易将新的运算符添加到中缀计算器中,只要它们是 只有单字符文字。词法分析器通过 将所有非数字字符作为标记返回,因此新的语法规则就足够了 添加新运算符。但我们想要更灵活的东西:内置 其语法形式为以下形式的函数:yylex

function_name (argument)

同时,我们将通过允许您为计算器添加内存 若要创建命名变量,请在其中存储值,以便稍后使用它们。 以下是使用多功能计算器的示例会话:

$ mfcalc
pi = 3.141592653589
⇒ 3.1415926536
sin(pi)
⇒ 0.0000000000
alpha = beta1 = 2.3
⇒ 2.3000000000
alpha
⇒ 2.3000000000
ln(alpha)
⇒ 0.8329091229
exp(ln(beta1))
⇒ 2.3000000000
$

请注意,允许多个赋值和嵌套函数调用。


2.5.1 声明mfcalc

以下是多功能的 C 和 Bison 声明 计算器。

%{
  #include <stdio.h>  /* For printf, etc. */
  #include <math.h>   /* For pow, used in the grammar. */
  #include "calc.h"   /* Contains definition of 'symrec'. */
  int yylex (void);
  void yyerror (char const *);
%}
%define api.value.type union /* Generate YYSTYPE from these types: */
%token <double>  NUM     /* Double precision number. */
%token <symrec*> VAR FUN /* Symbol table pointer: variable/function. */
%nterm <double>  exp

%precedence '='
%left '-' '+'
%left '*' '/'
%precedence NEG /* negation--unary minus */
%right '^'      /* exponentiation */

上面的语法只介绍了Bison语言的两个新功能。 这些功能允许语义值具有各种数据类型 (请参阅多个值类型)。

分配给变量的特殊值指定使用符号的数据定义符号 类型。Bison 将生成 to 的适当定义 存储这些值。union%defineapi.value.typeYYSTYPE

由于值现在可以具有各种类型,因此必须关联类型 使用其语义值的每个语法符号。这些符号是 、 、 和 。他们的声明是 使用其数据类型(放置在尖括号之间)进行扩充。为 实例中,则 的值存储在 中。NUMVARFUNexpNUMdouble

Bison 构造用于声明非终端符号, 就像用于声明令牌种类一样。以前我们做过 之前不使用,因为非终端符号通常 由定义它们的规则隐式声明。但必须 显式声明,以便我们可以指定其值类型。请参阅非终端符号%nterm%token%ntermexp


下一篇: , 上一篇: , 上一篇: 多功能计算器: mfcalc [内容][索引]]

2.5.2 语法规则mfcalc

以下是多功能计算器的语法规则。 它们中的大多数是直接从 ;三条规则, 那些提到 或 的,是新的。calcVARFUN

%% /* The grammar follows. */
input:
  %empty
| input line
;
line:
  '\n'
| exp '\n'   { printf ("%.10g\n", $1); }
| error '\n' { yyerrok;                }
;
exp:
  NUM
| VAR                { $$ = $1->value.var;              }
| VAR '=' exp        { $$ = $3; $1->value.var = $3;     }
| FUN '(' exp ')'    { $$ = $1->value.fun ($3);         }
| exp '+' exp        { $$ = $1 + $3;                    }
| exp '-' exp        { $$ = $1 - $3;                    }
| exp '*' exp        { $$ = $1 * $3;                    }
| exp '/' exp        { $$ = $1 / $3;                    }
| '-' exp  %prec NEG { $$ = -$2;                        }
| exp '^' exp        { $$ = pow ($1, $3);               }
| '(' exp ')'        { $$ = $2;                         }
;
/* End of grammar. */
%%

下一篇: , 上一篇: , 上一篇: 多功能计算器: mfcalc [内容][索引]]

2.5.3 符号表mfcalc

多功能计算器需要一个符号表来跟踪 变量和函数的名称和含义。这不会影响 语法规则(动作除外)或野牛声明,但它 需要一些额外的 C 函数来支持。

符号表本身由记录链表组成。其 定义,保留在标题中,如下所示。它 提供要放置在表中的函数或变量。calc.h

/* Function type. */
typedef double (func_t) (double);
/* Data type for links in the chain of symbols. */
struct symrec
{
  char *name;  /* name of symbol */
  int type;    /* type of symbol: either VAR or FUN */
  union
  {
    double var;    /* value of a VAR */
    func_t *fun;   /* value of a FUN */
  } value;
  struct symrec *next;  /* link field */
};
typedef struct symrec symrec;

/* The symbol table: a chain of 'struct symrec'. */
extern symrec *sym_table;

symrec *putsym (char const *name, int sym_type);
symrec *getsym (char const *name);

新版本的 will 调用来初始化 符号表:maininit_table

struct init
{
  char const *name;
  func_t *fun;
};
struct init const funs[] =
{
  { "atan", atan },
  { "cos",  cos  },
  { "exp",  exp  },
  { "ln",   log  },
  { "sin",  sin  },
  { "sqrt", sqrt },
  { 0, 0 },
};
/* The symbol table: a chain of 'struct symrec'. */
symrec *sym_table;
/* Put functions in table. */
static void
init_table (void)
{
  for (int i = 0; funs[i].name; i++)
    {
      symrec *ptr = putsym (funs[i].name, FUN);
      ptr->value.fun = funs[i].fun;
    }
}

只需编辑初始化列表并添加必要的包含 文件,您可以向计算器添加其他函数。

两个重要功能允许在 符号表。向函数传递名称和种类 ( 或 ) 的对象。对象是 链接到列表的前面,并返回指向该对象的指针。 该函数将传递要查找的符号的名称。如果 找到,则返回指向该符号的指针;否则返回零。putsymVARFUNgetsym

/* The mfcalc code assumes that malloc and realloc
   always succeed, and that integer calculations
   never overflow.  Production-quality code should
   not make these assumptions.  */
#include <assert.h>
#include <stdlib.h> /* malloc, realloc. */
#include <string.h> /* strlen. */
symrec *
putsym (char const *name, int sym_type)
{
  symrec *res = (symrec *) malloc (sizeof (symrec));
  res->name = strdup (name);
  res->type = sym_type;
  res->value.var = 0; /* Set value to 0 even if fun. */
  res->next = sym_table;
  sym_table = res;
  return res;
}
symrec *
getsym (char const *name)
{
  for (symrec *p = sym_table; p; p = p->next)
    if (strcmp (p->name, name) == 0)
      return p;
  return NULL;
}

下一篇: , Previous: , 上一篇: 多功能计算器: mfcalc [内容][索引]]

2.5.4 莱克斯mfcalc

该函数现在必须识别变量、数值和 单字符算术运算符。字母数字字符串 带有前导字母的字符被识别为变量或 函数取决于符号表对它们的说明。yylex

字符串将传递给符号表中的查找。如果 该名称将显示在表中,指针指向其位置和类型 ( 或 ) 返回到 。如果不是 已经在表中,则将其作为 using .同样,指针及其类型(必须是 )是 返回 .getsymVARFUNyyparseVARputsymVARyyparse

在处理数值和算术时不需要更改 中的运算符。yylex

#include <ctype.h>
#include <stddef.h>

int
yylex (void)
{
  int c = getchar ();

  /* Ignore white space, get first nonwhite character. */
  while (c == ' ' || c == '\t')
    c = getchar ();

  if (c == EOF)
    return YYEOF;
  /* Char starts a number => parse the number. */
  if (c == '.' || isdigit (c))
    {
      ungetc (c, stdin);
      if (scanf ("%lf", &yylval.NUM) != 1)
        abort ();
      return NUM;
    }

Bison 生成了一个定义,其中有一个名为存储符号值的成员。YYSTYPENUMNUM

  /* Char starts an identifier => read the name. */
  if (isalpha (c))
    {
      static ptrdiff_t bufsize = 0;
      static char *symbuf = 0;
      ptrdiff_t i = 0;
      do
        {
          /* If buffer is full, make it bigger. */
          if (bufsize <= i)
            {
              bufsize = 2 * bufsize + 40;
              symbuf = realloc (symbuf, (size_t) bufsize);
            }
          /* Add this character to the buffer. */
          symbuf[i++] = (char) c;
          /* Get another character. */
          c = getchar ();
        }
      while (isalnum (c));

      ungetc (c, stdin);
      symbuf[i] = '\0';
      symrec *s = getsym (symbuf);
      if (!s)
        s = putsym (symbuf, VAR);
      yylval.VAR = s; /* or yylval.FUN = s. */
      return s->type;
    }

  /* Any other character is a token by itself. */
  return c;
}

2.5.5 主要mfcalc

错误报告功能保持不变,新版本包括对用户需求的调用和设置(有关详细信息,请参阅跟踪解析器):maininit_tableyydebug

/* Called by yyparse on error. */
void yyerror (char const *s)
{
  fprintf (stderr, "%s\n", s);
}
int main (int argc, char const* argv[])
{
  /* Enable parse traces on option -p. */
  if (argc == 2 && strcmp(argv[1], "-p") == 0)
    yydebug = 1;
  init_table ();
  return yyparse ();
}

该程序既强大又灵活。您可以轻松添加新的 函数,修改此代码进行安装是一项简单的工作 预定义的变量,例如 OR 以及。pie


上一篇: , 上一篇: 示例 [内容][索引]

2.6 练习

  1. 将一些新函数添加到初始化列表中。math.h
  2. 添加另一个包含常量及其值的数组。然后修改以将这些常量添加到符号表中。这将是 最容易给出常量类型。init_tableVAR
  3. 如果用户引用未初始化,则使程序报告错误 变量,除非在其中存储值。

下一篇: , 上一篇: , 上一篇: Bison [Contents][Index]

3 Bison 语法文件

Bison 将上下文无关的语法规范作为输入,并生成一个 C-language 函数,用于识别语法的正确实例。

Bison 语法文件通常具有以 '' 结尾的名称。 请参阅调用 Bison.y


3.1 野牛语法大纲

Bison 语法文件有四个主要部分,此处显示为 适当的分隔符:

%{
  Prologue
%}

Bison declarations

%%
Grammar rules
%%

Epilogue

“”中括起来的注释可能出现在任何部分中。 作为 GNU 扩展,'' 引入了一个持续到结束的注释 的行。/* … *///


下一篇: ,上一篇:野牛语法大纲 [目录][索引]

3.1.1 序幕

该部分包含宏定义和 在语法规则中的操作中使用的函数和变量。 这些被复制到解析器实现文件的开头,以便 它们先于 . 的定义之前。您可以使用“' 从头文件中获取声明。如果你不需要任何 C 声明,您可以省略 '' 和 '' 分隔符 将此部分括起来。Prologueyyparse#include%{%}

该部分因首次出现 “'”位于注释、字符串文本或字符之外 不断。Prologue%}

您可能有多个部分,与 .这允许您拥有 C 和 Bison 声明 相互指代。例如,声明可以 使用头文件中定义的类型,您可能希望对函数进行原型设计 采用类型的参数。这可以通过两个块来完成,一个在声明之前,一个在声明之后。PrologueBison declarations%unionYYSTYPEPrologue%union

%{
  #define _GNU_SOURCE
  #include <stdio.h>
  #include "ptypes.h"
%}
%union {
  long n;
  tree t;  /* tree is defined in ptypes.h. */
}
%{
  static void print_token (yytoken_kind_t token, YYSTYPE val);
%}

当有疑问时,通常将序幕代码放在所有 Bison 之前会更安全 声明,而不是之后。例如,要素的任何定义 测试宏,例如或应该出现 在所有 Bison 声明之前,因为特征测试宏会影响 Bison 生成的指令的行为。_GNU_SOURCE_POSIX_C_SOURCE#include


下一篇: , 上一篇: , 上一篇: 野牛语法大纲 [目录][索引]

3.1.2 序幕替代方案

部分的功能通常可以是微妙的,并且 死板。作为替代方案,Bison 提供了一个指令 显式限定符字段,用于标识代码的用途和 因此,Bison 应该生成它的位置。对于 C/C++, 默认位置可以省略限定符,也可以是 、 、 之一。请参见%code 摘要Prologue%coderequiresprovidestop

再看一下上一节的示例:

%{
  #define _GNU_SOURCE
  #include <stdio.h>
  #include "ptypes.h"
%}
%union {
  long n;
  tree t;  /* tree is defined in ptypes.h. */
}
%{
  static void print_token (yytoken_kind_t token, YYSTYPE val);
%}

请注意,这里有两个部分,但有一个微妙的部分 它们的功能之间的区别。例如,如果您决定 覆盖 Bison 的默认定义 ,您应该在哪个部分编写新的 定义?5 你应该 首先编写它,因为 Bison 会将该代码插入解析器 实现文件之前的默认定义。在 您应该在哪个部分构建一个内部函数的原型,该函数接受 和 作为 参数?您应该在第二个中对其进行原型设计,因为 Bison 将插入 该代码 和 定义之后。PrologueYYLTYPEPrologueYYLTYPEProloguetrace_tokenYYLTYPEyytoken_kind_tYYLTYPEyytoken_kind_t

这两个部分在功能上的区别是 由他们之间的外观建立。这 行为引发了一些问题。首先,为什么 a 的位置会影响与 和 相关的定义?第二,如果没有呢?在那 情况下,第二种部分不可用。这 行为并不直观。Prologue%union%unionYYLTYPEyytoken_kind_t%unionPrologue

为了避免这种微妙的依赖关系,请使用 a 和 unqualified 重写示例。让我们继续添加 新定义和原型 同时:%union%code top%codeYYLTYPEtrace_token

%code top {
  #define _GNU_SOURCE
  #include <stdio.h>

  /* WARNING: The following code really belongs
   * in a '%code requires'; see below. */

  #include "ptypes.h"
  #define YYLTYPE YYLTYPE
  typedef struct YYLTYPE
  {
    int first_line;
    int first_column;
    int last_line;
    int last_column;
    char *filename;
  } YYLTYPE;
}

%union {
  long n;
  tree t;  /* tree is defined in ptypes.h. */
}
%code {
  static void print_token (yytoken_kind_t token, YYSTYPE val);
  static void trace_token (yytoken_kind_t token, YYLTYPE loc);
}

这样,不合格的人就实现了 与这两种部分的功能相同,但它是 始终明确您打算哪种类型。而且,这两种总是 即使在没有 .%code top%codePrologue%union

上面的块在逻辑上包含两部分。第一个 警告前的两行需要出现在解析器顶部附近 实现文件。警告后的第一行是必需的,因此也需要出现在解析器实现中 文件。但是,如果您已指示 Bison 生成解析器头文件 (参见 Bison Declaration Summary),您可能希望出现该行 在该头文件中的定义之前也是如此。该定义还应出现在解析器头文件中,以 覆盖那里的默认定义。%code topYYSTYPEYYSTYPEYYLTYPEYYLTYPE

换句话说,在上面的块中,除了前两个之外 行是 AND 定义所需的依赖项代码。 因此,它们属于一个或多个:%code topYYSTYPEYYLTYPE%code requires

%code top {
  #define _GNU_SOURCE
  #include <stdio.h>
}
%code requires {
  #include "ptypes.h"
}
%union {
  long n;
  tree t;  /* tree is defined in ptypes.h. */
}
%code requires {
  #define YYLTYPE YYLTYPE
  typedef struct YYLTYPE
  {
    int first_line;
    int first_column;
    int last_line;
    int last_column;
    char *filename;
  } YYLTYPE;
}
%code {
  static void print_token (yytoken_kind_t token, YYSTYPE val);
  static void trace_token (yytoken_kind_t token, YYLTYPE loc);
}

现在,Bison 将在解析器实现文件和解析器标头的 Bison 生成和定义之前插入新定义 文件。(按照同样的推理,也会是 适合编写自己的定义。#include "ptypes.h"YYLTYPEYYSTYPEYYLTYPE%code requiresYYSTYPE

当你为 和 编写依赖项代码时, 无论如何,您都应该更喜欢 是否指示 Bison 生成解析器头文件。当你是 编写仅需要 Bison 插入解析器的代码 实现文件,并且没有特殊需要出现在 该文件,您应该更喜欢不合格的 .这些做法将使代码的每个块的目的 对 Bison 和其他读取您的语法文件的开发人员来说是明确的。 按照这些做法,我们期望不合格的,并且是四种备选方案中最重要的。YYSTYPEYYLTYPE%code requires%code top%code%code top%code%code requiresPrologue

在开发解析器时的某个时刻,您可能会决定向解析器外部的模块提供。因此,您 可能希望 Bison 将原型插入到两个解析器标头中 文件和解析器实现文件。由于此函数不是 or 需要的依赖关系,它不会使 感觉将其原型移动到 .更重要的是, 因为它依赖于 和 是不够的。相反,将其原型从 不合格:trace_tokenYYSTYPEYYLTYPE%code requiresYYLTYPEyytoken_kind_t%code requires%code%code provides

%code top {
  #define _GNU_SOURCE
  #include <stdio.h>
}
%code requires {
  #include "ptypes.h"
}
%union {
  long n;
  tree t;  /* tree is defined in ptypes.h. */
}
%code requires {
  #define YYLTYPE YYLTYPE
  typedef struct YYLTYPE
  {
    int first_line;
    int first_column;
    int last_line;
    int last_column;
    char *filename;
  } YYLTYPE;
}
%code provides {
  void trace_token (yytoken_kind_t token, YYLTYPE loc);
}
%code {
  static void print_token (FILE *file, int token, YYSTYPE val);
}

Bison 会将原型插入到两个解析器中 头文件和 、 和 的定义之后的解析器实现文件。trace_tokenyytoken_kind_tYYLTYPEYYSTYPE

上面的示例小心翼翼地按照以下顺序编写指令: 生成的解析器实现和头文件的布局:、 、 和 。虽然您的语法文件通常可能更容易阅读,但如果 你也遵循这个顺序,野牛不需要它。取而代之的是,Bison 让 你选择一个对你有意义的组织。%code top%code requires%code provides%code

您可以在语法文件中多次声明这些指令中的任何一个。 在这种情况下,Bison 按声明顺序连接包含的代码。 这是这些指令之一的位置在 语法文件会影响其功能。

前两个属性的结果是,您可以更灵活地处理 整理语法文件。 例如,您可以按语义组织与语义类型相关的指令 类型:

%code requires { #include "type1.h" }
%union { type1 field1; }
%destructor { type1_free ($$); } <field1>
%printer { type1_print (yyo, $$); } <field1>
%code requires { #include "type2.h" }
%union { type2 field2; }
%destructor { type2_free ($$); } <field2>
%printer { type2_print (yyo, $$); } <field2>

您甚至可以将上述每个指令组放在 使用关联语义的规则集旁边的语法文件 类型。 (在规则部分中,您必须用 分号。 而且您不必担心 定义部分将对其功能产生不利影响 违反直觉的方式,只是因为它是第一位的。 使用部分是不可能的。%unionPrologue

本节关注的是解释四种备选方案相对于原始 Yacc 的优势。 但是,在大多数情况下,使用这些指令时,您不需要 考虑一下这里讨论的所有低级排序问题。 相反,您应该简单地使用这些指令来标记 根据其目的进行编码,并让 Bison 处理排序。 是最通用的标签。 根据需要将代码移动到 、 或 。ProloguePrologue%code%code requires%code provides%code top


下一篇: , 上一篇: , 上一篇: 野牛语法大纲 [目录][索引]

3.1.3 野牛申报部分

该部分包含定义 终端符号和非终端符号,指定优先级,依此类推。 在一些简单的语法中,您可能不需要任何声明。 见野牛宣言Bison declarations


下一篇: , 上一篇: , 上一篇: 野牛语法大纲 [目录][索引]

3.1.4 语法规则部分

语法规则部分包含一个或多个 Bison 语法 规则,仅此而已。请参阅语法规则

必须始终至少有一个语法规则,并且第一个 ''(在语法规则之前)甚至永远不会被省略 如果它是文件中的第一件事。%%


上一篇: ,上一篇:野牛语法大纲 [目录][索引]

3.1.5 结语

被逐字复制到解析器的末尾 实现文件,就像复制到 开始。这是放置任何东西的最方便的地方 希望在解析器实现文件中拥有,但不需要 在定义之前。例如,定义 的,经常去这里。因为 C 需要 函数在使用前要声明,通常需要声明 功能类似于序幕,甚至 如果您在结语中定义它们。请参阅分析器 C 语言接口EpiloguePrologueyyparseyylexyyerroryylexyyerror

如果最后一部分是空的,您可以省略分隔它的“” 从语法规则。%%

Bison 解析器本身包含许多宏和标识符,其名称 以“”或“”开头,因此最好避免使用 结语中的任何此类名称(本手册中记录的名称除外) 语法文件。yyYY


下一篇: , 上一篇: , 上一篇: 野牛语法文件 [内容][索引]

3.2 符号,终端和非终端

Bison 语法中的符号表示语法分类 的语言。

终端符号(也称为令牌类型)表示 语法等效标记的类。在语法中使用符号 规则表示允许该类中的令牌。符号是 在 Bison 解析器中由数字代码表示,该函数返回一个令牌类型代码来指示已是哪种令牌 读。你不需要知道代码值是什么;您可以使用符号 代表它。yylex

非终端符号在语法上代表一类 等效分组。符号名称用于编写语法规则。 按照惯例,它应该全部是小写的。

符号名称可以包含字母、下划线、句点和非首字母 数字和破折号。符号名称中的破折号是 GNU 扩展名,不兼容 与 POSIX Yacc 合作。句点和破折号使符号名称不太方便 与命名引用一起使用,这些引用需要在此类名称周围加上括号 (请参阅命名引用)。包含句点或破折号的终端符号 没什么意义:因为它们不是有效的符号(在大多数编程中 languages),它们不会导出为令牌名称。

语法中有三种写终端符号的方法:

  • 命名令牌类型是用标识符编写的,就像标识符一样 在C中。按照惯例,它应该全部大写。每个这样的名称必须是 使用 Bison 声明定义,例如 。请参阅令牌种类名称%token
  • 写入字符标记类型(或文字字符标记) 在语法中使用与 C 中相同的语法来表示字符常量;为 example,是字符标记类型。字符令牌类型 不需要声明,除非需要指定其语义值 数据类型(请参阅语义值的数据类型)、关联性或优先级 (请参阅运算符优先级)。'+'

    按照约定,字符标记类型仅用于表示以下标记: 由该特定字符组成。因此,令牌种类为 用于表示字符“”作为标记。没有什么可以强制执行这一点 约定,但如果你偏离它,你的程序会混淆其他 读者。'+'+

    可以使用 C 中字符文字中使用的所有常用转义序列 在 Bison 中也是如此,但您不得将 null 字符用作字符 字面意思,因为它的数字代码 0 表示输入结束 (请参阅 yylex 的调用约定)。此外,与标准 C 不同,三元组没有 野牛字符文字中的特殊含义,反斜杠换行符也不是 允许。

  • 文字字符串标记的编写方式类似于 C 字符串常量;为 example,是文本字符串标记。文本字符串标记 不需要声明,除非您需要指定其语义 值数据类型(请参阅语义值的数据类型)、关联性或优先级 (请参阅运算符优先级)。"<="

    您可以将文本字符串标记与符号名称关联为别名, 使用声明(请参阅令牌种类名称)。如果你不这样做 也就是说,词法分析器必须检索文本的标记代码 string token (请参阅 yylex 的调用约定)。%tokenyytname

    警告:文字字符串标记在 Yacc 中不起作用。

    按照惯例,文本字符串标记仅用于表示标记 它由该特定字符串组成。因此,您应该使用令牌 kind 表示字符串 '' 作为标记。野牛 不强制执行此约定,但如果您偏离它,则 阅读你的程序会感到困惑。"<="<=

    C 中字符串文字中使用的所有转义序列都可以在 Bison 也是如此,但您不能在 字符串文本。此外,与标准 C 不同,三元组没有特殊性 在 Bison 字符串文字中表示,也不允许使用反斜杠换行符。一个 文本字符串标记必须包含两个或更多字符;对于令牌 仅包含一个字符,请使用字符标记(见上文)。

您选择如何编写终端符号对其没有影响 语法意义。这仅取决于它在规则中出现的位置,并且 当解析器函数返回该符号时。

返回的值始终是终端之一 符号,但零值或负值表示输入结束。 无论您在语法规则中以哪种方式编写标记类型,您都编写 在 的定义中也是如此。数字代码 对于字符,令牌种类只是 字符,因此可以使用相同的值来生成 必要的代码,但您可能需要将其转换为以避免在签名的主机上进行签名扩展。 每个命名的标记类型都成为解析器实现中的 C 宏 文件,所以可以使用名称来代表代码。(这 这就是为什么句点在终端符号中没有意义的原因。请参阅 yylex 的调用约定yylexyylexyylexunsigned charcharyylex

如果在单独的文件中定义,则需要安排 令牌种类定义在那里可用。使用该选项 当您运行 Bison 时,它会将这些定义写入单独的 头文件,您可以将其包含在另一个文件中 需要它的源文件。请参阅调用 Bisonyylex-dname.tab.h

如果你想编写一个可以移植到任何标准 C 的语法 host,则只能使用从基本 标准 C 的执行字符集。这套由十个组成 digits、52 个小写和大写英文字母,以及 以下 C 语言字符串中的字符:

"\a\b\t\n\v\f\r !\"#%&'()*+,-./:;<=>?[\\]^_{|}~"

函数和 Bison 必须使用一致的字符集 以及字符标记的编码。例如,如果您在 ASCII 环境,然后编译并运行生成的 在使用不兼容字符集的环境中编程,例如 EBCDIC,生成的程序可能无法正常工作,因为表 由 Bison 生成的 ASCII 数值将假定为 字符令牌。软件发行版的标准做法是 包含 Bison 在 ASCII 环境,因此安装程序在以下平台上 与 ASCII 不兼容,必须先重建这些文件 编译它们。yylex

该符号是为错误恢复保留的终端符号 (请参阅错误恢复);您不应将其用于任何其他目的。 特别是,不应返回此值。默认值 错误标记的值为 256,除非将 256 显式分配给 带有声明的令牌之一。erroryylex%token


下一篇: , 上一篇: , 上一篇: 野牛语法文件 [目录][索引]

3.3 语法规则

Bison 语法是规则列表。


下一篇: ,上一篇: 语法规则 [内容][索引]

3.3.1 语法规则的语法

Bison 语法规则具有以下一般形式:

result: components…;

其中 是此规则描述的非终端符号, 并且是各种终端和非终端符号 由此规则组合在一起(请参阅符号、终端和非终端)。resultcomponents

例如

exp: exp '+' exp;

表示两个类型的分组,中间有一个 '' 标记, 可以组合成一个更大的类型分组。exp+exp

规则中的空白仅对分隔符号有意义。您可以添加 随心所欲地增加空白。

分散在组件之间可以确定 规则的语义。操作如下所示:actions

{C statements}

这是支撑代码的一个示例,即 C 代码被 大括号,很像 C 语言中的复合语句。 任何 C 标记序列,只要其大括号是平衡的。野牛 不直接检查支撑代码的正确性;它只是 将代码复制到解析器实现文件,其中 C 编译器可以检查它。

在大括号代码中,平衡大括号计数不受大括号的影响 在注释、字符串文本或字符常量中,但它是 受 C 二元字母 '' 和 '' 的影响,它们表示 括号。在顶层,支撑代码必须以 '' 结尾 而不是通过二分法。野牛不寻找三元组,所以如果支撑 代码使用三元图,应确保它们不会影响 大括号的嵌套或注释的边界、字符串文字或 字符常量。<%%>}

通常只有一个动作,它遵循组件。 请参阅操作

同一的多个规则可以单独编写,也可以 与竖线字符 '' 连接,如下所示:result|

result:
  rule1-components…
| rule2-components…
…
;

即使以这种方式连接,它们仍然被认为是不同的规则。


下一篇: , 上一篇: , 上一篇: 语法规则 [内容][索引]]

3.3.2 空规则

如果规则的右侧 () 为空。这意味着在前面的示例中可以匹配 空字符串。再举一个例子,下面是如何定义一个可选的 分号:componentsresult

semicolon.opt: | ";";

很容易看不到空规则,尤其是在使用时。该指令允许明确规则为 目的:|%empty

semicolon.opt:
  %empty
| ";"
;

标记非空规则是错误的。如果使用 运行,则将报告没有 的空规则。除非指定,否则使用将启用此警告。%empty-Wempty-rulebison%empty%empty-Wno-empty-rule

该指令是 Bison 扩展,它不适用于 雅克。为了保持与 POSIX Yacc 的兼容性,习惯上将 注释 '' 在每个没有组件的规则中:%empty/* empty */

semicolon.opt:
  /* empty */
| ";"
;

上一篇: , 上一篇: 语法规则 [内容][索引]

3.3.3 递归规则

当规则的非终端时,规则称为递归规则 也出现在它的右手边。几乎所有的 Bison 语法都需要 使用递归,因为这是定义任何 特定事物的数字。考虑这个递归定义 一个或多个表达式的逗号分隔序列:result

expseq1:
  exp
| expseq1 ',' exp
;

由于递归使用 是 中最左边的符号 右手边,我们称之为左递归。相比之下,这里 使用右递归定义相同的构造:expseq1

expseq1:
  exp
| exp ',' expseq1
;

任何类型的序列都可以使用左递归或右递归来定义 递归,但你应该始终使用左递归,因为它可以 解析具有有限堆栈空间的任意数量元素的序列。 右递归占用 Bison 堆栈上的空间与 序列中的元素数,因为所有元素都必须 在规则应用一次之前就转移到堆栈上。 有关进一步的解释,请参阅 Bison 解析器算法 这个。

间接递归或相互递归发生在以下结果 规则不会直接出现在其右侧,但确实会出现 在右侧出现的其他非终端的规则中 边。

例如:

expr:
  primary
| primary '+' primary
;
primary:
  constant
| '(' expr ')'
;

定义了两个相互递归的非终端,因为每个都引用 其他。


下一篇: , 上一篇: , 上一篇: 野牛语法文件 [内容][索引]

3.4 定义语言语义

语言的语法规则仅决定语法。语义 由与各种标记关联的语义值决定,并且 分组,以及识别各种分组时采取的行动。

例如,计算器计算正确,因为值 与每个表达式关联的是正确的数字;它正确添加 因为分组“”的操作是添加 与 和 关联的数字。x + yxy


下一篇: , 上一篇: 定义语言语义 [内容][索引]

3.4.1 语义值的数据类型

在一个简单的程序中,使用相同的数据类型可能就足够了 所有语言结构的语义值。在 RPN 和中缀计算器示例(请参阅反向波兰符号计算器)。

Bison 通常使用该类型作为语义值,如果您的程序 对所有语言构造使用相同的数据类型。指定一些其他 类型,如下定义变量:int%defineapi.value.type

%define api.value.type {double}

%define api.value.type {struct semantic_value_type}

的值应为不 包含括号或方括号。api.value.type

或者,在 C 语言中,不要依赖 Bison 的支持, 您可以依赖 C 预处理器并定义为宏:%defineYYSTYPE

#define YYSTYPE double

此宏定义必须放在语法文件的序言中 (见野牛语法大纲)。如果与 POSIX Yacc 的兼容性对您很重要, 使用这个。但请注意,Bison 无法知道 的值,而不是 即使它是否被定义,所以它无法提供服务。 此外,这仅适用于 C。YYSTYPE


下一篇: , 上一篇: , 上一篇: 定义语言语义 [内容][索引]]

3.4.2 多个值类型

在大多数程序中,不同类型的数据需要不同的数据类型 的令牌和分组。例如,数值常量可能需要 type or ,而字符串常量需要 type ,标识符可能需要指向 符号表。intlongchar *

要在一个解析器中对语义值使用多种数据类型,Bison 要求你做两件事:

  • 指定可能数据类型的整个集合。有几个 选项:
    • 让 Bison 根据您分配给符号的标签计算并集类型;
    • 使用野牛宣言(见《联合宣言》%union);
    • 将变量定义为并集类型 其成员是类型标记(请参阅提供结构化语义值类型%defineapi.value.type);
    • 使用 a 或 a 定义为 其成员名称是类型标记的联合类型。typedef#defineYYSTYPE
  • 为每个符号(终端或非终端)选择其中一种类型 使用哪些语义值。这是针对具有 Bison 声明的令牌(请参阅令牌种类名称)和 用于使用 / Bison 声明的分组 (请参阅非终端符号)。%token%nterm%type

下一篇: , 上一篇: , 上一篇: 定义语言语义 [内容][索引]

3.4.3 生成语义值类型

变量的特殊值指示 Bison 类型标签(与 和 指令一起使用)是真正的类型, 不是 的成员姓名。union%defineapi.value.type%token%nterm%typeYYSTYPE

例如:

%define api.value.type union
%token <int> INT "integer"
%token <int> 'n'
%nterm <int> expr
%token <char const *> ID "identifier"

生成适当的值以支持每个交易品种 类型。令牌的成员名称比具有 声明的标识符(例如 和 以上,但 不是 ) 是 。其他符号未指定 您不应该依赖的名称;而是依靠 C 强制转换来访问 具有适当类型的语义值:YYSTYPEYYSTYPEidINTID'n'id

/* For an "integer". */
yylval.INT = 42;
return INT;

/* For an 'n', also declared as int. */
*((int*)&yylval) = 42;
return 'n';

/* For an "identifier". */
yylval.ID = "42";
return ID;

如果定义了变量 (参见 %define Summary),则它也用于前缀 工会成员姓名。例如,使用 '':%defineapi.token.prefix%define api.token.prefix {TOK_}

/* For an "integer". */
yylval.TOK_INT = 42;
return TOK_INT;

如果启用了 (或 /),则此 Bison 扩展无法工作,因为 POSIX 要求 Yacc 将标记生成为宏(例如,“'”或“'”)。%yacc-y--yacc#define INT 258#define TOK_INT 258

为 C++ 提供了类似的功能,该功能还克服了 C++ 限制(禁止非平凡对象成为 A 的一部分): '',请参阅 C++ 变体union%define api.value.type variant


下一篇: , 上一篇: , 上一篇: 定义语言语义 [内容][索引]

3.4.4 《联盟宣言》

该声明指定了可能的整个集合 语义值的数据类型。关键字后跟 支撑代码包含与 C 中 a 相同的内容。%union%unionunion

例如:

%union {
  double val;
  symrec *tptr;
}

这表示两种替代类型是 和 。他们被赋予了名字和 ;使用这些名称 在 和 声明中选择 终端符号或非终端符号的类型之一(请参阅非终端符号)。doublesymrec *valtptr%token%nterm%type

作为 POSIX 的扩展,允许在 .为 例:%union

%union value {
  double val;
  symrec *tptr;
}

指定 union 标签,因此对应的 C 类型为 。如果未指定标记,则默认为 (请参阅 %define Summary)。valueunion valueYYSTYPE

作为 POSIX 的另一个扩展,您可以指定多个声明;它们的内容是串联的。但是,只有第一个声明可以指定标记。%union%union

请注意,与在 C 中进行声明不同,您不需要编写 右大括号后的分号。union


下一篇: , 上一篇: , 上一篇: 定义语言语义 [内容][索引]]

3.4.5 提供结构化的语义值类型

如果您的语法至少包含一个 '' ,则可以定义和使用自己的联合类型,而不是 标记。例如,您可以将以下内容放入头文件中:%unionYYSTYPE<type>parser.h

union YYSTYPE {
  double val;
  symrec *tptr;
};

然后你的语法可以使用以下内容来代替:%union

%{
#include "parser.h"
%}
%define api.value.type {union YYSTYPE}
%nterm <val> expr
%token <tptr> ID

实际上,您也可以提供一个相当的, 如果您想跟踪每个符号的信息(例如 如前所述)。structunion

您提供的类型甚至可以是结构化的,并包含指针,其中 案例 您提供的类型标签可能是复合的,带有 '' 和 '' 运营商。.->


3.4.6 操作

操作附带语法规则,并包含要执行的 C 代码 每次识别该规则的实例时。大多数操作的任务 是计算由规则构建的分组的语义值,从 与标记或较小分组关联的语义值。

操作由包含 C 语句的支撑代码组成,可以是 放置在规则中的任何位置; 它在该位置执行。大多数规则在 规则的末尾,跟随所有组件。中间的动作 规则很棘手,仅用于特殊目的(请参阅 Midrule 中的操作)。

动作中的 C 代码可以引用 规则与构造匹配的组件, 它代表第 th 个组件的值。语义 正在构造的分组的值为 。另外 符号的语义值可以通过命名 引用构造或 . Bison 将这两种结构都转化为 将操作复制到分析器中的适当类型 实现文件。 (或者,当它站立时 对于当前分组)被转换为可修改的左值,因此它 可以分配给。$nn$$$name$[name]$$$name

下面是一个典型的例子:

exp:
…
| exp '+' exp     { $$ = $1 + $3; }

或者,就命名引用而言:

exp[result]:
…
| exp[left] '+' exp[right]  { $result = $left + $right; }

此规则从两个较小的分组构造一个 通过加号令牌连接。在动作中,和 ( 和 ) 参考两个组件分组的语义值, 这是规则右侧的第一个和第三个符号。 总和存储在 () 中,因此它变为 语义值 规则刚刚识别的加法表达式。如果有一个 与“”标记关联的有用语义值,它可以是 称为 .expexp$1$3$left$rightexp$$$result+$2

有关使用命名引用的详细信息,请参阅命名引用 引用构造。

请注意,竖线字符“”实际上是一条规则 分隔符,操作附加到单个规则。这是一个 与 Flex 等工具的区别,其中 '' 代表任一 “或”或“与下一条规则相同的操作”。在 以下示例,仅当找到“”时才会触发该操作:||b

a-or-b: 'a'|'b'   { a_or_b_found = 1; };

如果未为规则指定操作,则 Bison 会提供默认值:.因此,规则中第一个符号的值 成为整个规则的值。当然,默认操作是 仅当两种数据类型匹配时才有效。没有有意义的默认值 空规则的操作;每个空规则都必须有一个显式操作 除非规则的值无关紧要。$$ = $1

$n允许使用零或负作为参考 到堆栈上的令牌和分组,然后与 当前规则。这是一种非常冒险的做法,并且要可靠地使用它 您必须确定应用规则的上下文。这里 是您可以可靠地使用它的情况:n

foo:
  expr bar '+' expr  { … }
| expr bar '-' expr  { … }
;
bar:
  %empty    { previous_expr = $0; }
;

只要仅以此处显示的方式使用,则始终指 的定义。bar$0exprbarfoo

如果满足以下条件,还可以访问前瞻令牌的语义值 any,来自语义操作。 此语义值存储在 中。 请参阅在操作中使用的特殊功能yylval


Next: , Previous: , Up: 定义语言语义 [Contents][Index]

3.4.7 操作中值的数据类型

如果为语义值选择了单一数据类型,则 and 构造始终具有该数据类型。$$$n

如果您曾经指定过各种数据类型,那么 必须为每个终端或非终端声明这些类型中的选择 可以具有语义值的符号。然后,每次使用 or 时,其数据类型由它引用的符号决定 在规则中。在此示例中,%union$$$n

exp:
  …
| exp '+' exp    { $$ = $1 + $3; }

$1并引用 的实例,所以它们都 为非终端符号声明数据类型。如果使用,它将为 终端符号,不管它是什么。$3expexp$2'+'

或者,您可以在引用值时指定数据类型, 通过在开头的“”之后插入“” 参考。例如,如果已定义类型,如下所示:<type>$

%union {
  int itype;
  double dtype;
}

然后你可以写来引用 规则为整数,或将其称为双精度。$<itype>1$<dtype>1


3.4.8 Midrule 中的操作

有时,将操作放在规则的中间很有用。 这些操作的编写方式与通常的规则结束操作类似,但它们 在解析器识别以下组件之前执行。


3.4.8.1 使用中间规则操作

中间规则操作可以引用它前面的组件 using ,但它可能不引用后续组件,因为 它在解析它们之前运行。$n

中间规则操作本身算作规则的组成部分之一。 当同一规则中稍后有另一个操作时,这会有所不同 (通常最后还有另一个):你必须计算动作 以及计算要在 中使用哪个数字时的符号。n$n

midrule 操作也可以具有语义值。操作可以设置 其值赋值为 ,并在规则中稍后执行操作 可以使用 来引用该值。由于没有符号 若要命名操作,无法声明值的数据类型 提前,所以你必须使用 '' 构造来 每次引用此值时指定数据类型。$$$n$<…>n

无法使用中间规则设置整个规则的值 动作,因为赋值没有这种效果。这 设置整个规则值的唯一方法是使用普通操作 在规则的末尾。$$

下面是一个假设编译器的示例,它处理类似于 '' 和 用于创建一个临时命名的变量 的持续时间。要解析这个结构,我们必须在解析时放入符号表中,然后 之后将其删除。这是如何完成的:letlet (variable) statementvariablestatementvariablestatement

stmt:
  "let" '(' var ')'
    {
      $<context>$ = push_context ();
      declare_variable ($3);
    }
  stmt
    {
      $$ = $6;
      pop_context ($<context>5);
    }

一旦“”被识别,第一个 操作运行。它保存当前语义上下文的副本( 可访问变量列表)作为其语义值,在数据类型联合中使用 alternative。然后,它调用以将新变量添加到该列表中。一旦 第一个动作完成,嵌入语句可以 解析。let (variable)contextdeclare_variablestmt

请注意,midrule 操作是组件编号 5,因此 '' 是 组件编号 6。命名引用可用于提高可读性 和可维护性(请参阅命名引用):stmt

stmt:
  "let" '(' var ')'
    {
      $<context>let = push_context ();
      declare_variable ($3);
    }[let]
  stmt
    {
      $$ = $6;
      pop_context ($<context>let);
    }

解析嵌入语句后,其语义值变为 整个 -statement 的值。然后语义值从 “先前操作”用于还原先前的变量列表。这 从列表中删除临时变量,使其不会 在解析程序的其余部分时似乎存在。letlet

因为 midrule 操作的语义值类型是未知的 Bison,基于类型的功能(例如,'','')可以 不起作用,这可能会导致内存泄漏。他们还禁止使用 在 C++ (请参阅 C++ 变体)。%printer%destructorvariantapi.value.type

有关解决此问题的一种方法,请参阅类型化中间规则操作,以及有关另一种方法:转换中间操作操作,请参阅类型化中间规则操作 变成常规行动。


下一篇: ,上一篇:,上一篇:Midrule 中的动作 [内容][索引]

3.4.8.2 类型化中间线操作

在上面的例子中,如果解析器在解析嵌入语句中的标记时启动了错误恢复(参见 Error Recovery),则 它可能会丢弃以前的语义上下文,而没有 恢复它。因此,需要一个析构函数 (参见释放丢弃的符号),而 Bison 需要 语义值 () 的类型来选择正确的析构函数。stmt$<context>5$<context>5context

作为 Yacc 的 midrule 操作的扩展,Bison 提供了一种键入方法 它们的语义值:指定其 type 标记(中间线前的 '' 行动。<...>

请考虑前面的示例,其中包含一个非类型的 midrule 操作:

stmt:
  "let" '(' var ')'
    {
      $<context>$ = push_context (); // ***
      declare_variable ($3);
    }
  stmt
    {
      $$ = $6;
      pop_context ($<context>5);     // ***
    }

相反,如果你写:

stmt:
  "let" '(' var ')'
    <context>{                       // ***
      $$ = push_context ();          // ***
      declare_variable ($3);
    }
  stmt
    {
      $$ = $6;
      pop_context ($5);              // ***
    }

然后正常工作(不再有泄漏! 可以使用 C++ s,并减少冗余(指定一次)。%printer%destructorvariant<context>


3.4.8.3 中间线动作转换

中间规则操作实际上转换为常规规则和操作。 Bison 生成的各种报告(文本、图形等,参见 Understanding Your Parser)揭示了这个翻译, 最好通过一个例子来解释。以下规则:

exp: { a(); } "b" { c(); } { d(); } "e" { f(); };

被翻译成:

$@1: %empty { a(); };
$@2: %empty { c(); };
$@3: %empty { d(); };
exp: $@1 "b" $@2 $@3 "e" { f(); };

使用新的非终端符号,其中是一个数字。$@nn

如果 midrule 操作使用 ,则应生成一个值,或者 (最终)操作使用 where 表示中间线 行动。在这种情况下,它的非终端被命名为:$$$nn@n

exp: { a(); } "b" { $$ = c(); } { d(); } "e" { f = $1; };

被翻译成

@1: %empty { a(); };
@2: %empty { $$ = c(); };
$@3: %empty { d(); };
exp: @1 "b" @2 $@3 "e" { f = $1; }

在上面的例子中可能有两个错误:第一个中间规则操作 不生成值(它不使用,虽然最终 action 使用它),并且不使用第二个的值(final 操作不使用 )。Bison在启用警告时会报告这些错误(请参阅调用Bison):$$$3midrule-value

$ bison -Wmidrule-value mid.y
mid.y:2.6-13: warning: unset value: $$
    2 | exp: { a(); } "b" { $$ = c(); } { d(); } "e" { f = $1; };
      |      ^~~~~~~~
mid.y:2.19-31: warning: unused value: $3
    2 | exp: { a(); } "b" { $$ = c(); } { d(); } "e" { f = $1; };
      |                   ^~~~~~~~~~~~~

有时将中间规则操作转换为常规操作很有用,例如, 考虑它们,或逃避它们的局限性。例如,作为 除了键入的 midrule 操作之外,您还可以隐藏 midrule 操作 在非终端符号内,并声明打印机和 析构函数 该符号:

%nterm <context> let
%destructor { pop_context ($$); } let
%printer { print_context (yyo, $$); } let
%%

stmt:
  let stmt
    {
      $$ = $2;
      pop_context ($let);
    };
let:
  "let" '(' var ')'
    {
      $let = push_context ();
      declare_variable ($var);
    };


3.4.8.4 由于 Midrule 操作引起的冲突

在规则被完全识别之前采取行动通常会导致 冲突,因为解析器必须提交解析才能执行 行动。例如,以下两个规则,不带中间规则操作, 可以在工作解析器中共存,因为解析器可以移动大括号 令牌,并在决定是否存在 声明与否:

compound:
  '{' declarations statements '}'
| '{' statements '}'
;

但是,当我们按如下方式添加 midrule 操作时,规则将变得不起作用:

compound:
  { prepare_for_local_variables (); }
     '{' declarations statements '}'
|    '{' statements '}'
;

现在,解析器被迫决定是否运行 midrule 操作 当它读得不超过开括号时。换句话说,它 必须承诺使用一条或另一条规则,而没有足够的规则 正确执行此操作的信息。(大括号令牌就是所谓的 此时的 Lookahead 令牌,因为解析器仍在 决定如何处理它。请参阅 Lookahead 令牌

您可能认为您可以通过将相同的 动作成两条规则,如下所示:

compound:
  { prepare_for_local_variables (); }
    '{' declarations statements '}'
| { prepare_for_local_variables (); }
    '{' statements '}'
;

但这无济于事,因为野牛没有意识到这两个动作 是相同的。(Bison 从不尝试理解动作中的 C 代码。

如果语法使得声明可以与 语句由第一个标记(在 C 中为 true),然后是一个解决方案 确实有效是将动作放在大括号之后,如下所示:

compound:
  '{' { prepare_for_local_variables (); }
    declarations statements '}'
| '{' statements '}'
;

现在是以下声明或语句的第一个标记, 无论如何,它都会告诉 Bison 使用哪条规则,仍然可以这样做。

另一种解决方案是将操作隐藏在非终端符号中,该符号 用作子例程:

subroutine:
  %empty  { prepare_for_local_variables (); }
;
compound:
  subroutine '{' declarations statements '}'
| subroutine '{' statements '}'
;

现在 Bison 可以在没有的情况下执行规则中的操作 决定最终将使用哪个规则。subroutinecompound


下一篇: , 上一篇: , 上一篇: 野牛语法文件 [内容][索引]

3.5 跟踪位置

虽然语法规则和语义操作足以写出一个完整的 函数式解析器,处理一些附加信息可能很有用, 尤其是符号位置。

位置的处理方式是通过提供数据类型来定义的,并且 规则匹配时要执行的操作。


下一页: ,向上:跟踪位置 [内容][索引]

3.5.1 位置的数据类型

为位置定义数据类型比为语义值定义数据类型要简单得多。 因为所有标记和分组始终使用相同的类型。位置类型 使用 '':%define api.location.type

%define api.location.type {location_t}

这在 C 生成的代码中定义了类型名称。未定义时,Bison 使用具有四个的默认结构类型 成员:YYLTYPEYYLTYPE

typedef struct YYLTYPE
{
  int first_line;
  int first_column;
  int last_line;
  int last_column;
} YYLTYPE;

在 C 中,您还可以通过定义一个名为 的宏来指定位置类型,就像您可以通过定义 宏(请参阅语义值的数据类型)。但是,而不是使用 宏,我们推荐 AND 变量。YYLTYPEYYSTYPEapi.value.typeapi.location.type%define

默认位置表示源文件中的一个范围,但这不是一个 要求。它可以是一个点,也可以只是一个行号,甚至更多 结构复杂。

使用默认位置类型时,Bison 将初始化所有这些字段 在解析开始时设置为 1。使用自定义位置类型进行初始化(或选择其他位置类型 initialization),使用指令。请参阅在解析之前执行操作yyllocyylloc%initial-action


下一篇: , 上一篇: , 上一篇: 追踪位置 [内容][索引]

3.5.2 操作和位置

操作不仅对定义语言语义有用,而且对 使用位置描述输出解析器的行为。

构建句法分组位置的最明显方法是非常 类似于语义值的计算方式。在给定的规则中,几个 构造可用于访问正在匹配的元素的位置。 右侧的第 个分量的位置是 ,而左侧分组的位置是 。n@n@$

此外,命名引用构造符号位置,也可用于寻址符号位置。 有关使用命名引用的详细信息,请参阅命名引用 引用构造。@name@[name]

下面是使用位置默认数据类型的基本示例:

exp:
  …
| exp '/' exp
    {
      @$.first_column = @1.first_column;
      @$.first_line = @1.first_line;
      @$.last_column = @3.last_column;
      @$.last_line = @3.last_line;
      if ($3)
        $$ = $1 / $3;
      else
        {
          $$ = 1;
          fprintf (stderr, "%d.%d-%d.%d: division by zero",
                   @3.first_line, @3.first_column,
                   @3.last_line, @3.last_column);
        }
    }

至于语义值,位置有一个默认操作,即 每次匹配规则时运行。它将 的开头设置为 第一个符号的开头,以及 的结尾 最后一个符号。@$@$

使用此默认操作,位置跟踪可以全自动。这 上面的例子只是这样重写:

exp:
  …
| exp '/' exp
    {
      if ($3)
        $$ = $1 / $3;
      else
        {
          $$ = 1;
          fprintf (stderr, "%d.%d-%d.%d: division by zero",
                   @3.first_line, @3.first_column,
                   @3.last_line, @3.last_column);
        }
    }

也可以访问前瞻令牌的位置(如果有), 从语义操作。 此位置存储在 中。 请参阅在操作中使用的特殊功能yylloc


下一页: , 上一篇: , 向上: 追踪位置 [内容][索引]

3.5.3 打印位置

使用默认位置类型时,调试跟踪报告符号的 位置。生成的解析器使用宏执行此操作。YYLOCATION_PRINT

宏:YYLOCATION_PRINT文件位置);

启用跟踪后,在(类型为“”)上打印(类型为“”)。禁用跟踪时不执行任何操作,或者 如果位置类型是用户定义的。locYYLTYPE const *fileFILE *

要使用用户定义的位置类型获取调试跟踪中的位置, 定义宏。例如:YYLOCATION_PRINT

#define YYLOCATION_PRINT   location_print

上一篇: , 上一篇: 跟踪位置 [内容][索引]

3.5.4 位置的默认操作

实际上,操作并不是计算位置的最佳位置。因为 位置比语义值要普遍得多,在 输出解析器,用于重新定义要为每个执行的默认操作 统治。每次规则 匹配,然后运行关联的操作。它也被调用 在处理语法错误时,计算错误的位置。 在报告无法解决的句法歧义之前,GLR 解析器以递归方式调用以计算位置 这种模棱两可。YYLLOC_DEFAULTYYLLOC_DEFAULT

大多数情况下,此宏足够通用,可以抑制位置 来自语义操作的专用代码。

宏采用三个参数。第一个是 分组的位置(计算结果)。当一个 规则匹配,则第二个参数标识 规则的所有右侧元素都匹配,以及第三个 参数是规则右侧的大小。 当 GLR 解析器报告歧义时,哪个候选者 它传递到的右侧未定义。 处理语法错误时,第二个参数标识位置 在错误处理过程中丢弃的符号,以及第三个 参数是丢弃的符号数。YYLLOC_DEFAULTYYLLOC_DEFAULT

默认情况下,是这样定义的:YYLLOC_DEFAULT

# define YYLLOC_DEFAULT(Cur, Rhs, N)                      \
do                                                        \
  if (N)                                                  \
    {                                                     \
      (Cur).first_line   = YYRHSLOC(Rhs, 1).first_line;   \
      (Cur).first_column = YYRHSLOC(Rhs, 1).first_column; \
      (Cur).last_line    = YYRHSLOC(Rhs, N).last_line;    \
      (Cur).last_column  = YYRHSLOC(Rhs, N).last_column;  \
    }                                                     \
  else                                                    \
    {                                                     \
      (Cur).first_line   = (Cur).last_line   =            \
        YYRHSLOC(Rhs, 0).last_line;                       \
      (Cur).first_column = (Cur).last_column =            \
        YYRHSLOC(Rhs, 0).last_column;                     \
    }                                                     \
while (0)

其中 是第 个符号的位置 in when 为正数,以及符号的位置 就在还原之前,当 和 都为零。YYRHSLOC (rhs, k)krhskkn

在定义时,应考虑:YYLLOC_DEFAULT

  • 所有参数都没有副作用。但是,只有第一个( result) 应由 修改。YYLLOC_DEFAULT
  • 为了与语义操作保持一致,在 右侧范围从 1 到 。当为零时,只有 0 是 有效索引,它指的是减少之前的符号。 在错误处理期间始终为正数。nnn
  • 如果需要,您的宏应该将其参数括起来,因为 实际参数可能不会用括号括起来。此外,您的 宏应该扩展为可以用作单个的东西 语句后跟分号。

下一篇: , 上一篇: , 上一篇: 野牛语法文件 [内容][索引]

3.6 命名引用

如前面各节所述,引用任何 语义值或位置是一个位置引用,它采用 形式 、 、 和 。然而 这样的参考不是很具有描述性。此外,如果您以后决定 在语法规则的右侧插入或删除符号,需要 重新编号此类引用可能很乏味且容易出错。$n$$@n@$

为了避免这些问题,您还可以引用语义值或位置 使用命名引用。首先,原始符号名称可以是 用作命名引用。例如:

invocation: op '(' args ')'
  { $invocation = new_invocation ($op, $args, @invocation); }

位置引用和命名引用可以任意混合使用。例如:

invocation: op '(' args ')'
  { $$ = new_invocation ($op, $args, @$); }

但是,有时常规符号名称是不够的,因为 歧义:

exp: exp '/' exp
  { $exp = $exp / $exp; } // $exp is ambiguous.

exp: exp '/' exp
  { $$ = $1 / $exp; } // One usage is ambiguous.

exp: exp '/' exp
  { $$ = $1 / $3; } // No error.

当发生歧义时,显式声明的名称可用于值和 地点。显式名称声明为符号后面的括号名称 在规则定义中的外观。例如:

exp[result]: exp[left] '/' exp[right]
  { $result = $left / $right; }

为了访问由 midrule 操作生成的语义值,一个 显式名称也可以通过在 Midrule 操作代码的右大括号:

exp[res]: exp[x] '+' {$left = $x;}[left] exp[right]
  { $res = $left + $right; }

在引用中,为了指定包含点和破折号的名称,显式 括号内的语法,必须使用:$[name]@[name]

if-stmt: "if" '(' expr ')' "then" then.stmt ';'
  { $[if-stmt] = new_if_stmt ($expr, $[then.stmt]); }

经常发生命名引用后跟点、破折号或其他的情况 C 标点符号和运算符。默认情况下,Bison 将读取 '' 作为对符号值的引用,后跟 '',即对语义领域的访问 价值。为了迫使野牛在其 整体作为语义值的名称,括号内的语法 必须使用 ''。$name.suffix$name.suffixsuffixname.suffix$[name.suffix]


下一篇: , 上一篇: , 上一篇: Bison 语法文件 [内容][索引]]

3.7 野牛宣言

Bison 语法的 Bison 声明部分定义符号 用于表述语义值的语法和数据类型。 请参阅符号、终端和非终端

必须声明所有令牌类型名称(但不包括单字符文字标记,如 and )。非终端符号必须是 如果需要指定要用于语义的数据类型,则已声明 值(请参阅多个值类型)。'+''*'

语法文件中的第一条规则还指定了开始符号,即 违约。如果您希望其他符号作为起始符号,则 必须显式声明它(请参阅语言和上下文无关语法)。


下一篇: ,上一篇: Bison Declarations [Contents][Index]

3.7.1 需要 Bison 的版本

您可能需要 Bison 的最低版本来处理语法。如果 未满足要求,退出并显示错误(退出 状态 63)。bison

%require "version"

某些已弃用的行为被禁用,某些行为需要:version

“3.2”(或更高)

C++ 已弃用的文件和 否 生成时间更长。position.hhstack.hh


下一篇: , 上一篇: 上一篇: Bison 声明 [内容][索引]

3.7.2 代币种类名称

声明令牌种类名称(终端符号)的基本方法如下:

%token name

Bison 会将其转换为解析器中的定义,以便 函数(如果它在此文件中)可以使用名称来 代表这种令牌类型的代码。yylexname

或者,您可以使用 、 、 、 或者代替 ,如果要指定 关联性和优先级。请参阅运算符优先级。但是,对于 为了清楚起见,我们建议仅使用这些指令来声明关联性 和优先级,而不是添加字符串别名、语义类型等。%left%right%precedence%nonassoc%token

您可以通过附加 非负十进制或十六进制整数值立即出现在字段中 在令牌名称之后:

%token NUM 300
%token XNUM 0x12d // a GNU extension

然而,通常最好让 Bison 为所有人选择数字代码 令牌种类。Bison 将自动选择与 彼此之间或与普通字符。

如果堆栈类型是联合,则必须扩充 或其他令牌声明以包含数据类型 用尖括号分隔的备选方案(请参阅多个值类型)。%token

例如:

%union {              /* define stack type */
  double val;
  symrec *tptr;
}
%token <val> NUM      /* define token NUM and its type */

您可以通过编写 声明末尾的文字字符串,声明 名称。例如:%token

%token ARROW "=>"

例如,C 语言的语法可能使用 等效的文字字符串标记:

%token  <operator>  OR      "||"
%token  <operator>  LE 134  "<="
%left  OR  "<="

将文本字符串和令牌种类名称等同起来后,就可以使用它们了 在进一步的声明或语法规则中可互换。该函数可以使用令牌名称或文本字符串来获取 令牌种类代码(请参阅 yylex 的调用约定)。yylex

字符串别名允许使用文本字符串提供更好的错误消息 而不是令牌名称,例如“”而不是“”。syntax error, unexpected ||, expecting number or (syntax error, unexpected OR, expecting NUM or LPAREN

字符串别名也可以标记为国际化(请参阅令牌国际化):

%token
    OR     "||"
    LPAREN "("
    RPAREN ")"
    '\n'   _("end of line")
  <double>
    NUM    _("number")

将用法语“”而不是“”产生。erreur de syntaxe, || inattendu, attendait nombre ou (erreur de syntaxe, || inattendu, attendait number ou (


下一篇: , 上一篇: , 上一篇: 野牛声明 [内容][索引]

3.7.3 运算符优先级

使用 、 、 或 声明声明 标记并指定其优先级和关联性, 一下子。这些称为优先级声明。 有关运算符的一般信息,请参阅运算符优先级 优先。%left%right%nonassoc%precedence

优先级声明的语法与以下语句几乎相同:%token

%left symbols

%left <type> symbols

事实上,这些声明中的任何一项都服务于 . 但除此之外,它们还指定了 所有 :%tokensymbols

  • 运算符的关联性决定了如何重复使用 运算符 nest:是否 '' 通过与 First 分组或与 First 分组来解析。 指定左关联性(与 First 分组)并指定右关联性 (与第一分组)。 指定 否 关联性,这意味着“”被视为语法错误。opx op y op zxyyz%leftxy%rightyz%nonassocx op y op z

    %precedence仅赋予 的优先级,并定义 完全没有关联性。使用它仅定义优先级,并保留任何 启用了由于关联性而导致的潜在冲突。symbols

  • 运算符的优先级决定了它如何与其他运算符嵌套。 在单个优先级声明中声明的所有令牌都相等 优先级和嵌套根据它们的关联性。当两个 在不同的优先级声明中声明的令牌关联,一个 稍后声明的优先级更高,并首先分组。

为了向后兼容,两者之间存在令人困惑的差异 参数列表和优先级声明。只有 a 可以将文本字符串与标记类型名称相关联。一个 precedence 声明始终将文本字符串解释为对 一个单独的令牌。例如:%token%token

%left  OR "<="         // Does not declare an alias.
%left  OR 134 "<=" 135 // Declares 134 for OR and 135 for "<=".

下一篇: , 上一篇: , 上一篇: 野牛声明 [内容][索引]

3.7.4 非终端符号

用于指定多个值类型时,必须 声明每个非终端符号的值类型,其值为 使用。这是通过声明完成的,如下所示:%union%type

%type <type> nonterminal

下面是一个非终端符号的名称,并且是您想要的替代符号中给出的名称 (见《联盟宣言》)。您可以在 如果它们具有相同的值类型,则声明相同。使用空间 分隔符号名称。nonterminaltype%union%type

虽然 POSIX Yacc 只允许非终端,但 Bison 接受 该指令也适用于终端符号。声明 专为非终端符号,使用更安全的:%type%nterm

%nterm <type> nonterminal

下一篇: , 上一篇: , 上一篇: 野牛声明 [内容][索引]

3.7.5 符号声明的语法

用于声明符号的各种指令的语法如下。

%token tag? ( id number? string? )+ ( tag ( id number? string? )+ )*
%left  tag? ( id number?)+ ( tag ( id number? )+ )*
%type  tag? ( id | char | string )+ ( tag ( id | char | string )+ )*
%nterm tag? id+ ( tag id+ )*

其中表示类型标记,例如“”,表示 标识符,例如“”、十进制或十六进制 整数,例如“”或“”,字符文字 例如 '',以及字符串文本,例如 ‘’.后缀量词是 ''(零或一), ''(零或更多)和 ''(一个或多个)。tag<ival>idNUMnumber3000x12dchar'+'string"number"?*+

指令 ,并表现 喜欢。%precedence%right%nonassoc%left


下一篇: , 上一篇: , 上一篇: 野牛声明 [内容][索引]

3.7.6 解析前执行操作

有时,解析器需要在解析之前执行一些初始化。 该指令允许此类任意代码。%initial-action

指令: %initial-action { code }

声明在每次调用解析之前必须调用 braced。可以使用(或)和 — 的初始值和位置 展望未来 — 以及 .codeyyparsecode$$$<tag>$@$%parse-param

例如,如果您的位置使用文件名,则可以使用

%parse-param { char const *file_name };
%initial-action
{
  @$.initialize (file_name);
};

下一篇: , 上一篇: , 上一篇: Bison 声明 [内容][索引]

3.7.7 释放丢弃的符号

在错误恢复期间(请参阅错误恢复),符号已推送 来自文件其余部分的堆栈和令牌将被丢弃,直到 解析器站起来。如果解析器内存不足,或者如果它 返回 ,或 ,所有 必须丢弃堆栈上的符号。即使解析器成功,它 必须放弃开始符号。YYABORTYYACCEPTYYNOMEM

当丢弃的符号传达基于堆的信息时,此内存是 失去。虽然这种行为对于批处理分析器是可以容忍的,例如 在传统的编译器中,对于像 shell 或 可以无限期解析和执行的协议实现。

该指令定义在以下情况下调用的代码 符号被自动丢弃。%destructor

指令: %destructor { code } 符号

每当解析器丢弃其中一个 .内 , (或 ) 指定与丢弃的符号关联的语义值,并指定其位置。其他解析器参数包括 也可用(参见解析器函数 yyparse)。codesymbolscode$$$<tag>$@$

当一个符号列在 中时,它称为 每个符号 。 您还可以通过列出语义类型来定义每个类型 标记。 在这种情况下,解析器将在丢弃任何 具有该语义类型标记的语法符号,除非该符号有自己的 每个符号 。symbols%destructor%destructor%destructorsymbolscode%destructor

最后,您可以定义两种不同类型的默认值。 您可以将每个 和 放在 语法文件中只有一个声明。 每当 丢弃任何没有 per-symbol 和 per-type 的用户定义语法符号。 对于此类语法,解析器使用 for 已正式声明语义类型标记的符号(、、 和 算作此类声明,但不计入此类声明)。 对于此类语法,解析器使用 for 没有声明的语义类型标记的符号。%destructor<*><>symbols%destructorcode%destructorcode<*>%token%nterm%type$<tag>$code<>

例如:

%union { char *string; }
%token <string> STRING1 STRING2
%nterm <string> string1 string2
%union { char character; }
%token <character> CHR
%nterm <character> chr
%token TAGLESS

%destructor { } <character>
%destructor { free ($$); } <*>
%destructor { free ($$); printf ("%d", @$.first_line); } STRING1 string1
%destructor { printf ("Discarding tagless symbol.\n"); } <>

保证,当解析器丢弃任何具有 语义类型标记,它传递其语义值 默认设置为。 但是,当解析器丢弃 a 或 a 时, 它使用第三个,它释放了它和 将其行号打印为 ( 仅调用一次)。 最后,解析器只在丢弃任何符号时打印一条消息, 例如,没有语义类型标签。<character>freeSTRING1string1%destructorstdoutfreeTAGLESS

Bison 生成的解析器仅调用默认的 s 用户定义的符号,而不是 Bison 定义的符号。 例如,解析器不会为特殊的 Bison 定义的符号 、 或(参见 Bison Symbols)调用任何一种默认值。 这些都不能在语法中引用。 它也不会为令牌调用(参见 Bison 符号),无论您是否 在语法中引用它。 但是,如果满足以下条件,它可能会为结束令牌(令牌 0)调用其中之一 将其从 to 重新定义,例如:%destructor%destructor$accept$undefined$enderror$endEND

%token END 0

最后,Bison 永远不会为未引用的 midrule 语义值(请参阅 Midrule 中的操作)。 也就是说,Bison 不认为中间线具有语义值,如果你 不要在中间线的操作中引用,或者(其中是中间线的右侧符号位置)在 该规则中的任何后续操作。但是,如果您确实引用了其中任何一个,则 Bison 生成的解析器将调用 它丢弃中间线符号。%destructor$$$nn<>%destructor


丢弃的符号如下:

  • 在错误恢复的第一阶段弹出的堆叠符号,
  • 错误恢复第二阶段的传入终端,
  • 当前展望和整个堆栈(当前 右侧符号)时,解析器立即返回,以及
  • 当前前瞻和整个堆栈(包括当前右侧 侧符号)当 C++ 解析器 () 捕获 中的异常时,lalr1.ccparse
  • 开始符号,当解析器成功时。

由于显式调用 、 或 错误恢复失败,解析器可以立即返回 或记忆力耗尽。YYABORTYYACCEPTYYNOMEM

显式触发语法的规则的右侧符号 错误过孔不会自动丢弃。通常 根据经验,只有当用户操作无法管理时,才会调用析构函数 记忆。YYERROR


下一篇: , 上一篇: , 上一篇: 野牛宣言 [内容][索引]

3.7.8 打印语义值

启用运行时跟踪后(请参阅跟踪解析器), 解析器报告其操作,例如减少。当涉及符号时 在报告操作时,仅显示其类型,因为解析器不能 了解语义值的格式。

该指令定义当符号 报道。它的语法与(请参阅释放丢弃的符号)相同。%printer%destructor

指令: %printer { code } 符号

每当解析器显示 .其中 表示输出流(C 中的 a、C++ 中的 an 和 D),(或 )指定与 符号及其位置。其他解析器参数包括 也可用(参见解析器函数 yyparse)。codesymbolscodeyyoFILE*std::ostream&stdout$$$<tag>$@$

它们被定义为 for (参见 Freeing Discarded Symbols.):它们可以是每个类型(例如, '')、每个符号(例如,''、''、'')、 按默认键入(即 ''),或未按默认键类型(即 ‘’).symbols%destructor<ival>expNUM"float"<*><>

例如:

%union { char *string; }
%token <string> STRING1 STRING2
%nterm <string> string1 string2
%union { char character; }
%token <character> CHR
%nterm <character> chr
%token TAGLESS

%printer { fprintf (yyo, "'%c'", $$); } <character>
%printer { fprintf (yyo, "&%p", $$); } <*>
%printer { fprintf (yyo, "\"%s\"", $$); } STRING1 string1
%printer { fprintf (yyo, "<>"); } <>

保证,当解析器打印任何具有语义类型的符号时 标记,它显示语义的地址 值。但是,当解析器显示 a 或 a 时,它会将其格式化为双引号中的字符串。它执行 在这种情况下只有第二个,所以它只打印一次。 最后,解析器为任何符号打印 '',例如 没有语义类型标记。有关完整示例,请参阅mfcalc 启用调试跟踪<character>STRING1string1%printer<>TAGLESS


下一篇: , 上一篇: , 上一篇: 野牛声明 [内容][索引]

3.7.9 禁止冲突警告

Bison 通常会在语法中出现任何冲突时发出警告 (参见 Shift/Reduce Conflicts),但大多数真正的语法 具有无害的转移/减少冲突,这些冲突在可预测的范围内得到解决 方式,并且很难消除。最好抑制 关于这些冲突的警告,除非冲突的数量 变化。您可以使用声明执行此操作。%expect

声明如下所示:

%expect n

这是一个十进制整数。宣言说应该有 是转移/减少冲突,没有减少/减少冲突。 如果 shift/reduce 冲突的数量不同,Bison 会报告错误 from ,或者如果存在任何 reduce/reduce 冲突。nnn

对于确定性解析器,reduce/reduce 冲突更多 严重,应完全消除。Bison 将始终报告 减少/减少这些分析器的冲突。使用 GLR 然而,解析器,这两种冲突都是例行公事;否则 无需使用 GLR 解析。因此,它是 还可以指定预期的减少/减少冲突数量 在 GLR 解析器中,使用声明:

%expect-rr n

您可能希望在您的 预期冲突的规范。为此,您还可以将修饰符附加到各个规则。 这些修饰符的解释与它们的用法不同 声明。当附加到规则时,它们指示状态数 其中规则涉及冲突。您需要咨询 用于确定要使用的适当数字的输出。 例如,对于以下语法片段,第一个规则出现在两个状态中,其中“”标记是 展望未来。确定这一点后,您可以使用修饰符记录此事实,如下所示:%expect%expect-rr-vempty_dims[%expect

dims:
  empty_dims
| '[' expr ']' dims
;

empty_dims:
  %empty   %expect 2
| empty_dims '[' ']'
;

中间规则操作会生成隐式规则,这些规则也会发生冲突 (请参阅由于 Midrule 操作导致的冲突)。附加 对隐式 中间规则操作的规则,放在操作之前。例如%expect%expect-rr

%glr-parser
%expect-rr 1

%%

clause:
  "condition" %expect-rr 1 { value_mode(); } '(' exprs ')'
| "condition" %expect-rr 1 { class_mode(); } '(' types ')'
;

在这里,适当的中间规则操作要等到之后才能确定 '' 令牌被移动。因此 这两个行动将相互冲突,我们应该期待一个 减少/减少每个冲突。(

通常,使用涉及以下步骤:%expect

  • 编译你的语法时没有 .使用该选项 获取冲突发生位置的详细列表。野牛也将 打印冲突数。%expect-v
  • 检查每个冲突以确保 Bison 的默认 分辨率是你真正想要的。如果没有,请重写语法和 回到起点。
  • 添加声明,从 野牛打印的数字。使用 GLR 解析器,还可以添加声明。%expectn%expect-rr
  • (可选)计算一个或多个状态的数目 将显示特定规则的冲突减少,并将这些数字相加 将受影响的规则设置为 AS 或修饰符 视情况而定。冲突的规则将显示在输出列表中 用方括号括起来,或者在减少/减少冲突的情况下, 作为与方括号具有相同前瞻符号的缩减 在相同状态下还原。%expect-rr%expect

现在,如果您引入意外冲突,Bison 将报告错误, 但否则会保持沉默。


3.7.10 开始符号

默认情况下,Bison 假定语法的起始符号是第一个 语法规范部分中指定的非终端。程序员 可以使用以下声明覆盖此限制:%start

%start symbol

3.7.11 纯(重入)解析器

重入计划是一个在以下过程中不会改变的计划 执行;换句话说,它完全由(只读)组成 法典。只要可以异步执行,重入就很重要; 例如,非重入程序可能无法安全地从信号调用 处理器。在具有多个控制线程的系统中,非可重入者 程序只能在联锁中调用。

通常,Bison 会生成一个不可重入的解析器。这是 适用于大多数用途,并允许与 Yacc 兼容。( 标准 Yacc 接口本质上是不可重入的,因为它们使用 静态分配的变量用于与 通信 包括 和 .)yylexyylvalyylloc

或者,您可以生成一个纯的可重入解析器。野牛 声明 '' 表示您希望解析器是 折返。它看起来像这样:%define api.pure

%define api.pure full

结果是通信变量和在 中成为局部变量,并且 调用约定用于词法分析器函数。 有关详细信息,请参阅纯解析器的调用约定。变量在拉取模式下变为本地变量,但在推送模式下变为本地变量。(请参阅错误报告功能 yyerror)。这 调用本身的约定是不变的。yylvalyyllocyyparseyylexyynerrsyyparseyypstateyyparse

解析器是否纯,与语法规则无关。 您可以从任何 有效的语法。


下一篇: , Previous: , Up: Bison 声明 [内容][索引]

3.7.12 推送解析器

拉取解析器被调用一次,它就会控制,直到其所有输入 完全解析。另一方面,推送解析器称为 每次提供新令牌时。

当推送解析器是 客户端应用程序中的主事件循环。这通常是 当需要触发主事件循环时,对 GUI 的要求 在一定时间段内。

通常,Bison 会生成一个拉取解析器。 以下 Bison 声明说您希望解析器是推送 解析器(参见 %define 摘要):

%define api.push-pull push

在几乎所有情况下,您都希望确保推送解析器也 纯解析器(请参阅纯(重入)解析器)。唯一的 你应该创建一个不纯的推送解析器的时间是向后 与不纯的 Yacc 拉取模式接口兼容。除非你知道 你在做什么,你的声明应该如下所示:

%define api.pure full
%define api.push-pull push

纯推送解析器之间存在一个显著的功能差异 和不纯的推送解析器。纯推送解析器具有 内存中同时存在许多相同类型的解析器的解析器实例。 不纯的推送解析器一次只能使用一个解析器。

当选择推送解析器时,Bison 将在 生成的解析器。 是生成的结构 解析器用于存储解析器的状态。 是 函数,以创建新的解析器实例。 将释放与相应解析器实例关联的资源。 最后,是每当 令牌可用于提供解析器。一个微不足道的例子 使用纯推送解析器如下所示:yypstateyypstate_newyypstate_deleteyypush_parse

int status;
yypstate *ps = yypstate_new ();
do {
  status = yypush_parse (ps, yylex (), NULL);
} while (status == YYPUSH_MORE);
yypstate_delete (ps);

如果用户决定使用不纯的推送解析器,那么关于 生成的解析器将更改。变量变为全局变量 变量,而不是函数中的局部变量。为 因此,函数的签名更改为 删除令牌作为参数。非重入推送解析器示例将 因此看起来像这样:yycharyypush_parseyypush_parse

extern int yychar;
int status;
yypstate *ps = yypstate_new ();
do {
  yychar = yylex ();
  status = yypush_parse (ps);
} while (status == YYPUSH_MORE);
yypstate_delete (ps);

就是这样。请注意,下一个标记被放入全局变量中,供下次调用该函数使用。yycharyypush_parse

Bison 还支持推送解析器接口和拉取解析器 接口。为了获得此功能, 您应该将 '' 声明替换为 '' 声明。这样做将创建所有 前面提到的符号以及两个额外的符号,以及 . 可以完全像往常一样使用 将被使用。但是,用户应注意,它是在 通过调用生成解析器。 这使得使用 '' 声明比正常函数慢。如果用户 调用函数,它将解析输入的其余部分 流。可以标记以选择子语法 然后是输入流的其余部分。如果你愿意 要在解析样式之间来回切换,您必须 编写自己的函数,知道何时停止查找 用于输入。使用该函数的示例将显示 喜欢这个:%define api.push-pull push%define api.push-pull bothyyparseyypull_parseyyparseyypull_parseyyparse%define api.push-pull bothyyparseyypull_parseyypush_parseyypull_parseyypull_parseyypull_parse

yypstate *ps = yypstate_new ();
yypull_parse (ps); /* Will call the lexer */
yypstate_delete (ps);

添加 '' 声明对 生成的带有 '' 的解析器,就像它所做的那样 ‘’.%define api.pure%define api.push-pull both%define api.push-pull push


Next: , Previous: , Up: Bison Declarations [Contents][Index]

3.7.13 野牛声明摘要

以下是用于定义语法的声明的摘要:

指令:%union

声明语义值可能具有的数据类型的集合 (见《联盟宣言》)。

指令:%token

声明没有优先级的终端符号(令牌种类名称) 或指定的关联性(请参阅令牌种类名称)。

指令:%right

声明右关联的终端符号(令牌种类名称) (请参阅运算符优先级)。

指令:%left

声明左关联的终端符号(令牌类型名称) (请参阅运算符优先级)。

指令:%nonassoc

声明非关联的终端符号(令牌种类名称) (请参阅运算符优先级)。 以关联的方式使用它是语法错误。

指令:%nterm

声明非终端符号的语义值类型(请参阅非终端符号)。

指令:%type

声明符号的语义值类型(请参阅非终端符号)。

指令:%start

指定语法的开始符号(请参阅开始符号)。

指令:%expect

声明预期的移位/减少冲突数,无论是总体冲突还是 对于给定规则 (请参阅禁止冲突警告)。

指令:%expect-rr

声明减少/减少冲突的预期数量,无论是总体冲突还是 对于给定规则 (请参阅禁止冲突警告)。


要更改 的行为,请使用以下命令 指令:bison

指令:%code {code}
指令:%code qualifier {code}

将逐字插入到输出解析器源中 默认位置或位于 指定的位置。 请参见%code 摘要codequalifier

指令:%debug

检测分析器中的跟踪。被“”淘汰。 请参阅跟踪分析器%define parse.trace

指令:%define 变量
指令:%define 变量
指令:%define 变量 {value}
指令:%define 变量value

定义一个变量来调整 Bison 的行为。请参阅 %define 摘要

指令:%defines
指令:%defines defines-file

的历史名称。请参见 %header%header

指令:%析构函数

指定分析器应如何回收与 丢弃的符号。请参阅释放丢弃的符号

指令:%file-prefix prefix

指定要用于所有 Bison 输出文件名的前缀。名称 被选择,就好像语法文件被命名为 一样。prefix.y

指令:%header

编写包含令牌种类名称定义的分析器头文件 在语法以及其他一些声明中定义。如果解析器 实现文件被命名,然后被命名为解析器头文件 被命名为 。name.cname.h

对于 C 解析器,解析器头文件声明 除非已定义为宏,或者您使用了标记而没有使用 。因此,如果你是 将(请参阅多个值类型)与需要 其他定义,或者如果您已定义宏或类型 定义(参见语义值的数据类型),您需要安排这些定义 传播到所有模块,例如,将它们放在先决条件中 解析器和任何其他模块都包含的标头 需要。YYSTYPEYYSTYPE<type>%union%unionYYSTYPEYYSTYPE

除非解析器是纯解析器,否则解析器头文件将声明为外部变量。请参阅纯(重入)解析器yylval

如果还使用了位置,则解析器头文件声明并使用类似于宏 和 的协议。请参阅追踪位置YYLTYPEyyllocYYSTYPEyylval

如果您希望将 的定义,因为通常需要能够引用 上述声明和令牌种类代码。请参阅标记的语义值yylexyylex

如果已声明 或 ,则输出 header 还包含他们的代码。 请参见%code 摘要%code requires%code provides

生成的标题受到 C 的保护,防止多次包含 预处理器保护: '',其中 和 是前缀(请参阅同一程序中的多个解析器)和 生成的文件名变为大写,每个系列都是非字母数字 字符转换为单个下划线。YY_PREFIX_FILE_INCLUDEDPREFIXFILE

例如,使用 '' 和 '',标头将按如下方式保护。%define api.prefix {calc}%header "lib/parse.h"

#ifndef YY_CALC_LIB_PARSE_H_INCLUDED
# define YY_CALC_LIB_PARSE_H_INCLUDED
...
#endif /* ! YY_CALC_LIB_PARSE_H_INCLUDED */

在 Bison 3.8 中引入。

指令:%header header-file

与上面相同,但保存在文件中。header-file

指令: %language language

指定生成的分析器的编程语言。现在 支持的语言包括 C、C++、D 和 Java。 是 不区分大小写。language

指令:%locations

生成处理位置的代码(请参阅在操作中使用的特殊功能)。这 一旦语法使用特殊的“”,就会启用模式 标记,但如果你的语法不使用它,使用 '' 允许 以获得更准确的语法错误消息。@n%locations

指令:%name-prefix prefix

被“”淘汰。请参阅同一程序中的多个解析器。有关 C++ 解析器,请参阅 '' 文档。%define api.prefix {prefix}%define api.namespace

重命名分析器中使用的外部符号,使其以“而不是”开头。在 C 中重命名的符号的精确列表 解析器是 、 、 、 、 、 和 (如果使用位置)。如果使用推送解析器,则 、 、 和 也将重命名。例如,如果使用 '',则名称变为 、 和 依此类推。prefixyyyyparseyylexyyerroryynerrsyylvalyycharyydebugyyllocyypush_parseyypull_parseyypstateyypstate_newyypstate_delete%name-prefix "c_"c_parsec_lex

与定义相反,有些符号不会被重命名 例如,通过 、 、 、 。api.prefix%name-prefixYYDEBUGYYTOKENTYPEyytoken_kind_tYYSTYPEYYLTYPE

指令:%no-lines

不要在分析器中生成任何预处理器命令 实现文件。通常,Bison 在解析器中编写这些命令 实现文件,以便 C 编译器和调试器将关联 源文件(语法文件)的错误和目标代码。这 指令使它们将错误与解析器实现相关联 文件,将其视为独立的源文件。#line

指令:%output “file

在 中生成解析器实现。file

指令:%pure-parser

“”的弃用版本(参见 %define Summary),Bison 对此更谨慎地警告 不合理使用。%define api.pure

指令:%require version

需要 Bison 的 Vison版本或更高版本。请参阅需要 Bison 版本version

指令:%skeleton file

指定要使用的骨架。

如果不包含 ,则为骨架的名称 文件。 如果是这样,则是一个绝对文件名或相对于 语法文件的目录。 这类似于大多数 shell 解析命令的方式。file/filefile

指令:%token-table

此功能已过时,请在新项目中避免使用。

在分析器实现文件中生成令牌名称数组。这 数组的名称是 ; 是 其内部 Bison 令牌代码为 的令牌。前三个 元素对应于预定义的标记 、 和 ;在这些之后是 语法文件中定义的符号。yytnameyytname[i]iyytname"$end""error""$undefined"

表中的名称包括表示 野牛中的令牌。对于单字符文本和文本字符串,这 包括周围的引号字符和任何转义序列。为 例如,Bison 单字符文字对应于 三个字符的名称,在 C 中表示为 ;和野牛 两个字符的文字字符串对应于五个字符 name,在 C 中表示为 .'+'"'+'""\\/""\"\\\\/\""

指定 时,Bison 还会生成宏定义 对于宏 、 和 ,和 :%token-tableYYNTOKENSYYNNTSYYNRULESYYNSTATES

YYNTOKENS

终端符号的数量,即最高令牌代码加一。

YYNNTS

非终端符号的数量。

YYNRULES

语法规则的数量,

YYNSTATES

解析器状态的数量(请参阅解析器状态)。

下面是用于查找多字符令牌的代码, 假设令牌的字符存储在 中 并假设令牌不包含任何字符,例如“” 这需要逃避。yytnametoken_buffer"

for (int i = 0; i < YYNTOKENS; i++)
  if (yytname[i]
      && yytname[i][0] == '"'
      && ! strncmp (yytname[i] + 1, token_buffer,
                    strlen (token_buffer))
      && yytname[i][strlen (token_buffer) + 1] == '"'
      && yytname[i][strlen (token_buffer) + 2] == 0)
    break;

不鼓励使用此方法:字符串别名的主要目的是伪造 良好的错误消息,没有描述关键字的拼写。另外 在运行时查找令牌类型会产生(小但明显的)成本。

最后,与变量的 和 值不兼容。%token-tablecustomdetailedparse.error%define

指令:%verbose

编写一个额外的输出文件,其中包含分析器的详细描述 状态以及在该状态下对每种类型的 Lookahead 令牌执行的操作。 有关详细信息,请参阅了解分析器

指令:%yacc

假装给出了选项 (参见 --yacc),即模仿 Yacc,包括其 命名约定。只有骨架才有意义。有关详细信息,请参阅调整解析器--yaccyacc.c

当然,作为 Bison 的扩展,有点 自相矛盾......%yacc


下一篇: , 上一篇: , 上一篇: 野牛声明 [内容][索引]

3.7.14 %define 摘要

野牛行为的许多特征可以通过以下方式控制 为要素分配单个值。由于历史原因,一些这样的 功能由专用指令赋值,例如 分配开始符号。但是,更新的此类功能是相关的 变量,由指令赋值:%start%define

指令:%define 变量
指令:%define 变量
指令:%define 变量 {value}
指令:%define 变量value

定义为 。variablevalue

值的类型取决于语法。大括号表示 目标语言(例如,命名空间、类型等)。关键字值(否 分隔符)表示有限选择(例如,特征的变体)。字符串 值表示剩余的情况(例如,文件名)。

如果 a 由多个定义,则为错误 次,但请参见 -D name[=value]。variable%define

本节的其余部分总结了接受的变量和值。%define

有些采用布尔值。在这种情况下,野牛会抱怨 如果变量定义不满足以下四个条件之一 条件:variable

  1. valuetrue
  2. value被省略(或被指定)。 这相当于 。""true
  3. value是。false
  4. variable从未定义过。 在本例中,Bison 选择默认值。

什么是被接受的,以及它们的含义和默认值 值,取决于所选的目标语言和/或分析器骨架 (见野牛宣言摘要,见野牛宣言摘要)。 未接受的 s 会产生错误。下面介绍了一些被接受的 s。variablevariablevariable

指令:%define api.filename.type {type}
  • 语言: C++
  • 目的: 在 Bison 的默认位置和位置中定义文件名的类型 类型。请参阅公开位置类
  • 接受的价值观: 任何可打印(通过流)和可比较(带有 和 )的类型。==!=
  • 默认值:.const std::string
  • 历史: 在 Bison 2.0 中引入(与 default),在 Bison 3.7 中重命名为 (默认)。filename_typestd::stringapi.filename.typeconst std::string
指令:%define api.header.include {“header.h”}
指令:%define api.header.include {<header.h>}
  • 语言: C (yacc.c)
  • 用途:指定生成的分析器应如何包含生成的标头。

    从历史上看,当使用 option 或时,会生成一个标头并将其的精确副本粘贴到 生成的解析器实现文件。从 Bison 3.6 开始,它是 d 作为 '',而不是重复的,除非是 '',见下文。-d--headerbison#include"basename.h"filey.tab

    该变量允许控制如何生成 解析器生成的标头。例如:api.header.include#include

    %define api.header.include {"parse.h"}
    

    %define api.header.include {<parser/parse.h>}
    

    使用不会更改生成的名称 标头,只是它的包含方式。api.header.include

    要解决 Automake(与 一起运行)的限制,当输出文件为 时,未预定义 。将其定义为 避免重复。ylwrapbison--yaccapi.header.includey.tab.c

  • 接受的价值观: 的参数。#include
  • 默认值: '',除非头文件是 , 其中 是生成的标头的名称,没有 目录部分。例如,使用 '',默认为 '',而不是 ‘’."header-basename"y.tab.hheader-basenamebison -d calc/parse.yapi.header.include"parse.h""calc/parse.h"
  • 历史: 在 Bison 3.4 中引入。默认为“”,因为 Bison 3.7,除非头文件是 ."basename.h"y.tab.h
指令:%define api.location.file file
指令:%define api.location.file none
  • 语言: C++
  • 目的: 定义 Bison 的默认位置和位置的文件的名称 生成类型。请参阅公开位置类
  • 接受的价值观:
    none

    如果启用了位置,则在头文件中生成 和 类的定义,否则为 分析器实现。positionlocation%header

    "file"

    生成 和 类的定义 在。此文件名可以是相对的(与解析器文件所在的位置 output) 或绝对值。positionlocationfile

  • 默认值: 如果未启用位置,或者用户位置类型为 指定(参见)。否则,Bison's 将在 (请参阅 C++ 位置) 中生成。api.location.typelocationlocation.hh
  • 历史: 在 Bison 3.2 中引入。
指令:%define api.location.include {“file”}
指令:%define api.location.include {<file>}
  • 语言: C++
  • 目的: 指定如何包含定义 and 类的生成文件。当类向应用程序/库的其余部分公开时,这是有道理的 另一个目录。请参阅公开位置类positionlocationlocation
  • 接受的值:. 的参数。#include
  • 默认值: '' 其中 是目录部分 输出。例如,如果被给予。"dir/location.hh"dirsrc/parse--output=src/parse/parser.cc
  • 历史: 在 Bison 3.2 中引入。
指令:%define api.location.type {type}
  • 语言:C、C++、Java
  • 用途:定义位置类型。 请参阅位置的数据类型用户定义的位置类型
  • 接受的值: String
  • 默认值:无
  • 历史: 在 Bison 2.7 for C++ 和 Java 中引入,在 Bison 3.4 for C 中引入 最初在 Bison 2.5 和 2.6 中命名。location_type
指令:%define api.namespace {namespace}
  • 语言: C++
  • 用途:指定分析器类的命名空间。 例如,如果指定:
    %define api.namespace {foo::bar}
    

    Bison 在参考文献中逐字使用,例如:foo::bar

    foo::bar::parser::value_type
    

    但是,要打开命名空间,Bison 会删除任何前导,然后 对任何剩余的匹配项进行拆分:::

    namespace foo { namespace bar {
      class position;
      class location;
    } }
    
  • 接受的价值观: 任何不带尾随的绝对或相对 C++ 命名空间引用。例如,或 ."::""foo""::foo::bar"
  • 默认值:,除非使用了过时的“” 命令。yy%name-prefix "prefix"
指令:%define api.parser.class {name}
  • 语言: C++, Java, D
  • 目的: 分析器类的名称。
  • 接受的价值观: 任何有效标识符。
  • 默认值: 在 C++ 中,.在 D 和 Java 中,或者(参见 Java Bison 接口)。parserYYParserapi.prefixParser
  • 历史: 在 Bison 3.3 中引入以替换 .parser_class_name
指令:%define api.prefix {prefix}
  • 语言:C、C++、Java
  • 用途:重命名导出的符号。 请参阅同一程序中的多个解析器
  • 接受的值: String
  • 默认值:对于 Java,否则。YYyy
  • 历史: 在 Bison 2.6 中引入,其参数用双引号括起来。使用大括号 从 Bison 3.0 开始(向后仍支持双引号 兼容性)。
指令:%define api.pure purity
  • 语言: C
  • 目的:请求纯(可重入)解析器程序。 请参阅纯(重入)解析器
  • 接受的值: , ,truefalsefull

    可以省略该值:这等效于按原样指定 布尔值的情况。true

    使用时,解析器将重入。这 更改签名(请参阅纯解析器的调用约定),以及激活位置跟踪时的签名,如下所示 下面。%define api.pure fullyylexyyerror

    该值与该值非常相似,唯一的 不同之处在于 在 Yacc 解析器上没有签名,出于历史原因。truefullyyerror%parse-param

    也就是说,如果传递了 '',则 的原型是:%locations %define api.pureyyerror

    void yyerror (char const *msg);                 // Yacc parsers.
    void yyerror (YYLTYPE *locp, char const *msg);  // GLR parsers.
    

    但是,如果 '' 是 used,则两个解析器具有相同的签名:%locations %define api.pure %parse-param {int *nastiness}

    void yyerror (YYLTYPE *llocp, int *nastiness, char const *msg);
    

    (请参阅错误报告功能 yyerror)

  • 默认值:false
  • 历史: 该值是在 Bison 2.7 中引入的full
指令:%define api.push-pull kind
  • 语言:C(仅限确定性解析器)、D、Java
  • 用途:请求拉取解析器和/或推送解析器。 请参阅推送解析器
  • 接受的值: , ,pullpushboth
  • 默认值:pull
指令:%define api.symbol.prefix {prefix}
  • 语言:全部
  • 目的: 为符号种类的名称添加前缀。例如
    %define api.symbol.prefix {S_}
    %token FILE for ERROR
    %%
    start: FILE for ERROR;
    

    在 C 语言中生成以下定义:

    /* Symbol kind.  */
    enum yysymbol_kind_t
    {
      S_YYEMPTY = -2,   /* No symbol.  */
      S_YYEOF = 0,      /* $end  */
      S_YYERROR = 1,    /* error  */
      S_YYUNDEF = 2,    /* $undefined  */
      S_FILE = 3,       /* FILE  */
      S_for = 4,        /* for  */
      S_ERROR = 5,      /* ERROR  */
      S_YYACCEPT = 6,   /* $accept  */
      S_start = 7       /* start  */
    };
    
  • 接受的价值观: 任何非空字符串。必须是目标语言的有效标识符 (通常是字母、下划线和 -的非空序列,而不是在 begin— 数字)。

    空前缀(通常)无效:

    • 在 C 中,它会与宏发生冲突,并且 潜在的令牌类型定义和符号类型定义将 碰撞;YYERROR
    • 未命名的符号(例如“”)的名称以数字开头;'+'
    • 即使在具有作用域枚举的语言(如 Java)中,空前缀也是 危险:符号名称可能与目标语言关键字发生冲突,或者 与班级的其他成员一起。SymbolKind
  • 默认值:在 C 中,在 C++ 和 Java 中,在 D 中为空。YYSYMBOL_S_
  • 历史: 在 Bison 3.6 中引入。
指令:%define api.token.constructor
  • 语言: C++,D
  • 目的: 请求将符号作为一个整体(类型、值和可能 位置)在扫描仪中。在 C++ 的情况下,它仅在以下情况下才有效 启用基于变体的语义值(请参阅 C++ 变体),有关详细信息,请参阅完整符号。在 D 中,令牌构造函数同时使用这两种方法 '' 和 ''.%union%define api.value.type union
  • 接受的价值观: 布尔。
  • 默认值:false
  • 历史: 在 Bison 3.0 中引入。
指令:%define api.token.prefix {prefix}
  • 语言:全部
  • 目的: 在生成令牌名称时,在 目标语言。例如
    %define api.token.prefix {TOK_}
    %token FILE for ERROR
    %%
    start: FILE for ERROR;
    

    在生成的源文件中生成符号 、 和 的定义。特别是扫描仪 必须使用这些带前缀的标记名称,而语法本身可能仍使用 短名称(如上面给出的示例规则所示)。生成的 信息文件 (, , ) 不是 由此前缀修改。TOK_FILETOK_forTOK_ERROR*.output*.xml*.gv

    Bison 还为语义值并集的生成成员名称添加前缀。 有关详细信息,请参阅生成语义值类型 详。

    有关完整示例,请参阅 Calc++ ParserCalc++ Scanner

  • 接受的价值观: 任何字符串。必须是目标语言中的有效标识符前缀 (通常,可能是空的字母、下划线和 —not at 开头 - 数字)。
  • 默认值: 空
  • 历史: 在 Bison 3.0 中引入。
指令:%define api.token.raw
  • 语言: 都
  • 目的: 输出文件通常定义具有 Yacc 兼容令牌代码的令牌类型的枚举:从 257 开始的序列号,除了 对于代表自己的单字符标记(例如,在 ASCII 中, '' 编号为 65)。但是,解析器使用符号类型,这些符号类型从 0 开始按顺序分配数字。因此,每次 扫描程序返回一个(外部)令牌类型,它必须映射到 (内部)符号种类。'a'

    设置后,令牌种类的代码被强制为 与符号种类相吻合。这样可以节省每个要映射的令牌的表查找 它们从令牌种类到符号种类,也节省了一代 的映射表。增益通常适中,但在极端情况下 (非常简单的用户操作),可以观察到 10% 的改进。api.token.raw

    设置后,语法不能使用字符文字 (例如“”)。api.token.raw'a'

  • 接受的值: Boolean.
  • 默认值:在 D 中,否则truefalse
  • 历史: 在 Bison 3.5 中引入。最初在 Bison 1.25 中引入 '',但从未工作过,并在 Bison 1.29 中被删除。%raw
指令:%define api.value.automove
  • 语言: C++
  • 目的: 设规则右侧的语义值的出现次数为 隐式上交的右值。启用后,语法如下:
    exp:
      "number"     { $$ = make_number ($1); }
    | exp "+" exp  { $$ = make_binary (add, $1, $3); }
    | "(" exp ")"  { $$ = $2; }
    

    实际上编译得好像你写过:

    exp:
      "number"     { $$ = make_number (std::move ($1)); }
    | exp "+" exp  { $$ = make_binary (add,
                                       std::move ($1),
                                       std::move ($3)); }
    | "(" exp ")"  { $$ = std::move ($2); }
    

    在启用自动移动的情况下多次使用某个值通常是错误的。 例如,而不是:

    exp: "twice" exp  { $$ = make_binary (add, $2, $2); }
    

    写:

    exp: "twice" exp { auto v = $2; $$ = make_binary (add, v, v); }
    

    在其中一个上使用是很诱人的,但是 C++ 中的参数计算顺序未指定。std::movev

  • 接受的价值观: 布尔。
  • 默认值:false
  • 历史: 在 Bison 3.2 中引入
指令:%define api.value.type 支持
指令:%define api.value.type {type}
  • 语言: 都
  • 目的: 语义值的类型。
  • 接受的价值观:
    '{}'

    这种语法根本没有语义价值。这没有得到适当的支持 还。

    联合指令”(C、C++、D)

    由于该指令,该类型是定义的。你没有 在这种情况下定义,使用就足够了。 见《国际电联宣言》。 例如:%unionapi.value.type%union

    %define api.value.type union-directive
    %union
    {
      int ival;
      char *sval;
    }
    %token <ival> INT "integer"
    %token <sval> STR "string"
    
    联合”(C、C++)

    符号使用类型名称定义,Bison 将从中生成 .例如:union

    %define api.value.type union
    %token <int> INT "integer"
    %token <char *> STR "string"
    

    大多数 C++ 对象不能存储在 ,使用 '' 相反。unionvariant

    变体”(C++)

    这类似于 ,但使用特殊的存储技术来 允许使用任何类型的 C++ 对象。例如:union

    %define api.value.type variant
    %token <int> INT "integer"
    %token <std::string> STR "string"
    

    请参阅 C++ 变体

    '{类型}'

    将此用作语义值。type

    %code requires
    {
      struct my_value
      {
        enum
        {
          is_int, is_str
        } kind;
        union
        {
          int ival;
          char *sval;
        } u;
      };
    }
    %define api.value.type {struct my_value}
    %token <u.ival> INT "integer"
    %token <u.sval> STR "string"
    
  • 默认值:
    • - union-directive如果使用,否则......%union
    • - int如果使用 type 标记(即 '' 或 ''),否则......%token <type>…%nterm <type>…
    • -定义。
  • 历史: 在 Bison 3.0 中引入。仅在 2.3b 中为 Java 引入。stype
指令:%define api.value.union.name 名称
  • 语言: C
  • 目的: 生成的标记(不是 的名称)。此变量设置为使用“”时。没有明确的理由给这个工会起个名字。uniontypedefid%union id
  • 接受的价值观: 任何有效标识符。
  • 默认值:.YYSTYPE
  • 历史: 在 Bison 3.0.3 中引入。
指令:%define lr.default-reduction
  • 语言: 全部
  • 用途:指定允许的状态类型 包含默认减少。请参阅默认减少
  • 接受的值: , ,mostconsistentaccepting
  • 默认值:
    • accepting如果是 .lr.typecanonical-lr
    • most否则。
  • 历史: 在 2.5 中引入,在 3.0 中重命名为。lr.default-reductionslr.default-reduction
指令:%define lr.keep-unreachable-state
  • 语言: 全部
  • 目的:请求 Bison 允许无法访问的解析器状态 保留在分析器表中。请参阅无法访问的状态
  • 接受的值: Boolean
  • 默认值:false
  • 历史: 在 2.3b 中引入,在 2.5 和 3.0 中重命名为 3.0。lr.keep_unreachable_stateslr.keep-unreachable-stateslr.keep-unreachable-state
指令:%define lr.type type
  • 语言: 全部
  • 用途:指定 LR(1) 系列。请参阅 LR 表构造
  • 接受的值: , ,lalrielrcanonical-lr
  • 默认值:lalr
指令:%define namespace {namespace}

已过时api.namespace

指令:%define parse.assert
  • 语言:C、C++
  • 用途:发出运行时断言以捕获无效用途。 在 C 语言中,解析器实现中的一些重要不变量是 启用此选项时选中。

    在 C++ 中,使用变体时(请参阅 C++ 变体),符号必须是 正确建造和销毁。此选项检查这些约束 使用运行时类型信息 (RTTI)。因此,生成的代码不能 在禁用 RTTI 的情况下进行编译(通过编译器选项,例如 )。-fno-rtti

  • 接受的值: Boolean
  • 默认值:false
指令:%define parse.error 详细程度
  • 语言: 都
  • 目的: 控制语法错误消息的生成。请参阅错误报告
  • 接受的价值观:
    • simple传递给的错误消息只是 .yyerror"syntax error"
    • detailed错误消息报告意外令牌,也可能是预期的令牌。 但是,当未启用 LAC 时,此报告通常不正确 (见 LAC)。支持令牌名称国际化。
    • verbose与 相似(但不如)。D 分析器不支持此值。detailed

      错误消息报告意外令牌,也可能是预期的令牌。 但是,当未启用 LAC 时,此报告通常不正确 (见 LAC)。

      不支持令牌国际化。在 令牌别名不可移植。

    • custom用户负责通过定义函数来生成语法错误消息。请参阅语法错误报告功能yyreport_syntax_erroryyreport_syntax_error
  • 默认值:simple
  • 历史: 在 3.0 中引入,支持 和 。值,并在 3.6 中引入。simpleverbosecustomdetailed
指令: %define parse.lac
  • 语言:C/C++(仅限确定性解析器)、D 和 Java。
  • 目的:启用 LAC(前瞻校正)以改进 语法错误处理。请参见 LAC
  • 接受的值: ,nonefull
  • 默认值:none
指令:%define parse.trace
  • 语言:C、C++、D、Java
  • 用途:需要分析器检测进行跟踪。 请参阅跟踪分析器

    在 C/C++ 中,定义宏(或使用 ''),请参阅同一程序中的多个解析器)改为 1(如果尚未定义),以便调试工具是 编译。YYDEBUGprefixDEBUG%define api.prefix {prefix}

  • 接受的值: Boolean
  • 默认值:false
指令:%define parser_class_name {name}

已过时api.parser.class


上一篇: , 上一篇: 野牛声明 [内容][索引]

3.7.15 %代码摘要

该指令将代码逐字插入到输出中 解析器源位于预定义位置集中的任何一个位置。因此,它服务于 作为传统 Yacc 的灵活且用户友好的替代品 序幕。本节总结了 各种目标语言的功能 由野牛支持。详细讨论如何使用代替 C/C++ 以及为什么使用 这样做是有利的,请参阅序幕替代方案%code%{code%}%code%code%{code%}

指令:%code {code}

这是指令的无条件形式。它 在与语言相关的默认位置逐字插入 在解析器实现中。%codecode

对于 C/C++,默认位置是分析器实现文件 在分析器头文件的常规内容之后。因此, 在大多数情况下,不合格的表格会被替换。%{code%}

对于 D 和 Java,默认位置位于解析器类内。

指令:%code qualifier {code}

这是指令的限定形式。 确定 的目的,因此 Bison 应插入它的位置。也就是说,如果需要 指定不属于 非合格表单选择的默认位置,使用 这种形式。%codequalifiercodecode%code

对于任何特定的限定词或不合格的形式,如果有 指令的多次出现,Bison 连接 按其在语法中出现的顺序排列的指定代码 文件。%code

并非所有目标语言都接受所有限定词。未接受 限定符产生错误。一些公认的限定词是:

requires
  • 语言: C, C++
  • 目的: 这是编写值所需的依赖项代码的最佳位置,并且 位置类型(和 C 格式)。换言之, 这是定义指令中引用的类型的最佳位置。 在 C 语言中,如果你用来覆盖 Bison 的默认值和定义,那么它也是最好的地方。但是你 应该宁愿和 .YYSTYPEYYLTYPE%union#defineYYSTYPEYYLTYPE%defineapi.value.typeapi.location.type
  • 地点: 解析器头文件和解析器实现文件之前的 Bison 生成的值和位置类型的定义(和 C 语言)。YYSTYPEYYLTYPE
provides
  • 语言: C, C++
  • 目的:这是编写其他定义和 应提供给其他模块的声明。
  • 地点: 解析器头文件和解析器实现文件在 Bison 生成的值和位置类型(和 C 语言)以及令牌定义。YYSTYPEYYLTYPE
top
  • 语言: C, C++
  • 目的:不合格的或通常应该比 .然而 有时需要插入更靠近顶部的代码 解析器实现文件。例如:%code%code requires%code top
    %code top {
      #define _GNU_SOURCE
      #include <stdio.h>
    }
    
  • 位置:靠近分析器实现文件的顶部。
imports
  • 语言: D, Java
  • 目的:这是编写 Java import 指令的最佳位置。D 语法 允许在整个代码中使用 import 语句。
  • 位置:任何 Java 包指令和 在任何类定义之前。任何类定义之前的分析器 D 文件。

虽然我们说插入位置与语言有关,但它们确实如此 从技术上讲,依赖于骨架。非标准骨架的编写者 但是,应根据行为选择其位置 标准野牛骨架。


3.8 同一程序中的多个解析器

大多数使用 Bison 的程序只解析一种语言,因此包含 只有一个 Bison 解析器。但是,如果您想解析多种语言,该怎么办 使用相同的程序?然后你需要避免名称冲突 函数和变量的不同定义,例如 、 。要使用来自同一编译单元的不同解析器,您需要 还需要避免类型和宏上的冲突(例如,) 导出到生成的标头中。yyparseyylvalYYSTYPE

执行此操作的简单方法是定义变量。使用不同的 s,可以保证 标头在包含在一起时不会发生冲突,并且编译的对象 也可以链接在一起。指定 ''(或传递选项,请参阅调用 Bison)重命名接口函数和 Bison 解析器的变量开始,而不是 '',以及所有以(即大写)开头的宏,而不是 ''。%defineapi.prefixapi.prefix%define api.prefix {prefix}-Dapi.prefix={prefix}prefixyyPREFIXprefixYY

重命名的符号包括 、 、 、 、 和 。如果使用推送解析器,则 、 、 和 也将重命名。重命名的宏包括 、 和 ,处理 具体来说,更多关于这个的信息见下文。yyparseyylexyyerroryynerrsyylvalyyllocyycharyydebugyypush_parseyypull_parseyypstateyypstate_newyypstate_deleteYYSTYPEYYLTYPEYYDEBUG

例如,如果使用 '',则名称将变为 、 、 ...、 、 等 上。%define api.prefix {c}cparseclexCSTYPECLTYPE

Flex 用户必须更新生成函数的签名。由于 Flex 扫描仪通常包含生成的标头 解析器(获取令牌的定义等),最方便 方法是在以下部分中插入声明:yylexyylexprovides

%define api.prefix {c}
// Emitted in the header file, after the definition of YYSTYPE.
%code provides
{
  // Tell Flex the expected prototype of yylex.
  #define YY_DECL                             \
    int clex (CSTYPE *yylval, CLTYPE *yylloc)

  // Declare the scanner.
  YY_DECL;
}

该变量以两种不同的方式工作。 在实现文件中,它的工作原理是将宏定义添加到 解析器实现文件的开头,定义为 ,依此类推:%defineapi.prefixyyparseprefixparse

#define YYSTYPE CTYPE
#define yyparse cparse
#define yylval  clval
...
YYSTYPE yylval;
int yyparse (void);

这有效地将整个解析器中的一个名称替换为另一个名称 实现文件,因此“原始”名称 (, , ...) 也可以在解析器实现文件中使用。yylexYYSTYPE

但是,在解析器头文件中,符号被定义为重命名,用于 实例:

extern CSTYPE clval;
int cparse (void);

该宏通常用于启用跟踪支持 解析 器。为了遵守此传统,使用时,(未重命名)用作默认值:YYDEBUGapi.prefixYYDEBUG

/* Debug traces. */
#ifndef CDEBUG
# if defined YYDEBUG
#  if YYDEBUG
#   define CDEBUG 1
#  else
#   define CDEBUG 0
#  endif
# else
#  define CDEBUG 0
# endif
#endif
#if CDEBUG
extern int cdebug;
#endif


在 Bison 2.6 之前,类似的功能由 过时的指令(参见野牛符号)和 选项(请参阅输出文件)。api.prefix%name-prefix--name-prefix


下一篇: , 上一篇: , 上一篇: Bison [内容][索引]

4 解析器 C 语言接口

Bison 解析器实际上是一个名为 的 C 函数。在这里,我们 描述 和 的接口约定 它需要使用的函数。yyparseyyparse

请记住,解析器使用许多以 '' 和 '' 用于内部目的。如果您使用这样的 动作或结语中的标识符(本手册中的标识符除外) 在语法文件中,您可能会遇到麻烦。yyYY


下一篇: ,向上: 解析器C语言接口内容】【索引]

4.1 解析器函数yyparse

调用该函数以进行分析。这 函数读取令牌,执行操作,并最终在 遇到输入结束或不可恢复的语法错误。您还可以 编写一个指示立即返回的操作 无需进一步阅读。yyparseyyparse

函数: int yyparse void

如果解析成功,则返回的值为 0(返回 是由于输入结束)。yyparse

如果由于无效输入(即输入)而解析失败,则值为 1 包含语法错误或导致 调用。YYABORT

如果由于内存耗尽而分析失败,则该值为 2。

在操作中,您可以使用以下方法立即返回 这些宏:yyparse

宏:YYACCEPT

立即返回值为 0(报告成功)。

宏:YYABORT

立即返回值为 1(报告失败)。

宏:YYNOMEM

立即返回值 2(报告内存耗尽)。

如果使用可重入解析器,则可以选择传递其他 以可重入的方式向其提供参数信息。为此,请使用 声明:%parse-param

指令: %parse-param {argument-declaration} ...

声明一个或多个是附加参数。 声明时使用 功能或原型。中的最后一个标识符必须是参数名称。argument-declarationyyparseargument-declarationargument-declaration

下面是一个示例。在解析器中写下以下内容:

%parse-param {int *nastiness} {int *randomness}

然后像这样调用解析器:

{
  int nastiness, randomness;
  …  /* Store proper data in nastiness and randomness. */
  value = yyparse (&nastiness, &randomness);
  …
}

在语法操作中,使用如下表达式来引用数据:

exp: …    { …; *randomness += 1; … }

使用以下方法:

%parse-param {int *randomness}

结果如下:

void yyerror (int *randomness, const char *msg);
int  yyparse (int *randomness);

或者,如果两者兼而有之(或只是) 并使用:%define api.pure full%define api.pure%locations

void yyerror (YYLTYPE *llocp, int *randomness, const char *msg);
int  yyparse (int *randomness);

4.2 推送解析器接口

调用该函数以创建新的分析器实例。 如果 '' 或使用“”声明。请参阅推送解析器yypstate_new%define api.push-pull push%define api.push-pull both

功能:yypstate* yypstate_new void

如果有可用内存,则返回有效的解析器实例,否则返回 0。 在不纯模式下,如果解析器实例当前为 分配。

调用该函数以删除分析器实例。 如果 '' 或 '' 声明。 请参阅推送解析器yypstate_delete%define api.push-pull push%define api.push-pull both

功能:void yypstate_delete yypstate *yyps

回收与解析器实例关联的内存。打完这个电话后,你 不应再尝试使用解析器实例。

调用函数来分析单个令牌。这 如果 '' 或 '' 声明。请参阅推送解析器yypush_parse%define api.push-pull push%define api.push-pull both

功能:int yypush_parse yypstate *yyps

返回的值与 for 相同,但存在以下例外:如果更多输入是 需要完成语法分析。yypush_parseyyparseYYPUSH_MORE

返回后,可以咨询实例。为 实例检查是否有(可能已恢复) 语法错误。yypush_parseyynerrs

After 返回 以外的状态 , 解析器实例可以重用于新的解析。yypush_parseYYPUSH_MOREyyps

即使在发生错误后,解析器状态也是可重用的,这一事实简化了 再使用。例如,解析每一行输入的计算器应用程序 因为表达式可以继续重用相同的内容,即使输入 无效。yyps

调用该函数以分析输入的其余部分 流。如果 '' 使用声明。请参阅推送解析器yypull_parse%define api.push-pull both

函数: int yypull_parse yypstate *yyps

返回的值与 返回的值相同。yypull_parseyyparse

解析器实例可以重用于新的解析。yyps

函数: int yypstate_expected_tokens const yypstate *yyps, yysymbol_kind_t argv[]int argc

填充预期的标记,其中从不包含 、 或 。argvYYSYMBOL_YYEMPTYYYSYMBOL_YYerrorYYSYMBOL_YYUNDEF

永远不要把更多的元素放进去,在成功上 返回存储在 中的令牌数。如果还有更多 预期的令牌比 ,填满并返回 0. 如果没有预期的令牌,则也返回 0,但设置为 。argcargvargvargcargvargcargv[0]YYSYMBOL_YYEMPTY

启用 LAC 后,可能会在错误时返回负数, 例如内存耗尽。YYENOMEM

如果为 null,则返回存储所有可能文件所需的大小 值,它总是小于 。argvYYNTOKENS


下一篇: , 上一篇: , 上一篇: 解析器C语言接口 [内容][索引]

4.3 词汇分析器功能yylex

词法分析器函数 ,可识别来自 输入流,并将其返回到解析器。野牛不创造 此功能自动;你必须写它,这样才能 打电话给它。该函数有时称为词法扫描程序。yylexyyparse

在简单的程序中,通常在Bison的末尾定义 语法文件。如果在单独的源文件中定义,则 需要安排令牌种类的定义在那里可用。待办事项 这样,在运行 Bison 时使用该选项,以便它将写入 这些定义将添加到单独的分析器头文件中,您可以将其包含在其他源文件中 需要它。请参阅调用 Bisonyylexyylex-dname.tab.h


4.3.1 调用约定yylex

返回的值必须是 它刚刚找到的那种代币;零值或负值表示 输入结束。yylex

当标记类型在语法规则中由名称引用时,该名称 在解析器实现中,文件成为枚举的枚举器,其定义是枚举的正确数字代码 令牌种类。因此,应使用名称来指示该类型。 请参阅符号、终端和非终端yytoken_kind_tyylex

当字符文字在语法规则中引用标记时, 该字符的数字代码也是令牌类型的代码。所以可以简单地返回该字符代码,可能转换为以避免符号扩展。空字符不得 以这种方式使用,因为它的代码为零,这意味着输入结束。yylexunsigned char

下面是一个示例,显示了这些内容:

int
yylex (void)
{
  …
  if (c == EOF)    /* Detect end-of-input. */
    return YYEOF;
  …
  else if (c == '+' || c == '-')
    return c;      /* Assume token kind for '+' is '+'. */
  …
  else
    return INT;    /* Return the kind of the token. */
  …
}

此接口经过精心设计,可以在不更改 定义的情况下使用实用程序的输出。lexyylex


4.3.2 特殊代币

除了用户定义的令牌外,Bison 还会生成一些特殊令牌 可能会回来。yylex

令牌表示文件的结尾,并向解析器发出信号 之后什么都没有了。请参阅 yylex 的调用约定,了解 例。YYEOF

返回告诉解析器发现了一些词法错误。 它会发出一条关于“无效令牌”的错误消息,然后输入 error-recovery(请参阅错误恢复)。返回未知令牌种类 导致完全相同的行为。YYUNDEF

返回要求分析器在不发出错误消息的情况下输入错误恢复。这样,词法分析器可以 生成有关无效输入的准确错误消息(某些 解析器不能做),但受益于 解析 器。YYerror

int
yylex (void)
{
  …
  switch (c)
    {
      …
      case '0': case '1': case '2': case '3': case '4':
      case '5': case '6': case '7': case '8': case '9':
        …
        return TOK_NUM;
      …
      case EOF:
        return YYEOF;
      default:
        yyerror ("syntax error: invalid character: %c", c);
        return YYerror;
    }
}

下一篇: , 上一篇: , 上一篇: 词汇分析器函数 yylex [Contents][Index]

4.3.3 通过字符串文字查找标记

如果语法使用文字字符串标记,则有两种方法可以确定它们的标记类型代码:yylex

  • 如果语法将符号标记名称定义为文本的别名 字符串标记,可以像所有其他名称一样使用这些符号名称。 在这种情况下,语法文件中使用文本字符串标记具有 对 .yylexyylex

    这是首选方法。

  • yylex可以在表中搜索多字符令牌。不建议使用此方法:字符串别名的主要用途是 伪造良好的错误消息,而不是描述关键字的拼写。在 此外,在运行时查找令牌类型会产生 (小但 显着)成本。yytname

    仅当使用声明时,才会生成该表。参见野牛宣言摘要yytname%token-table


4.3.4 代币的语义值

在普通(非重入)解析器中,令牌的语义值必须 存储到全局变量中。当您仅使用 语义值的一种数据类型具有该类型。因此,如果 类型为(默认值),您可以将其写成:yylvalyylvalintyylex

  …
  yylval = value;  /* Put value onto Bison stack. */
  return INT;      /* Return the kind of the token. */
  …

当您使用多种数据类型时,的类型是联合制作的 从宣言(见《联盟宣言》)。所以当你存储 令牌的值,您必须使用适当的联合成员。如果声明如下所示:yylval%union%union

%union {
  int intval;
  double val;
  symrec *tptr;
}

那么代码可能如下所示:yylex

  …
  yylval.intval = value; /* Put value onto Bison stack. */
  return INT;            /* Return the kind of the token. */
  …

4.3.5 代币的文本位置

如果您使用的是“”功能(请参阅跟踪位置) 在跟踪标记和分组的文本位置的操作中, 那么,您必须在 中提供此信息。该函数期望找到刚刚解析的标记的文本位置 在全局变量 中。所以必须存储适当的 该变量中的数据。@nyylexyyparseyyllocyylex

默认情况下,的值是一个结构,您只需要 初始化操作将使用的成员。这 四个成员分别称为 、 和 。请注意,使用 功能使解析器明显变慢。yyllocfirst_linefirst_columnlast_linelast_column

的数据类型名称为 。yyllocYYLTYPE


4.3.6 纯解析器的调用约定

当您使用 Bison 声明请求 纯的、可重入的解析器,全局通信变量,不能使用。(请参阅纯(重入)解析器。在这样的解析器中,两者 全局变量被作为参数传递给 的指针替换。您必须声明它们,如下所示,并传递信息 通过这些指针存储它来返回。%define api.pure fullyylvalyyllocyylex

int
yylex (YYSTYPE *lvalp, YYLTYPE *llocp)
{
  …
  *lvalp = value;  /* Put value onto Bison stack. */
  return INT;      /* Return the kind of the token. */
  …
}

如果语法文件不使用“”结构来引用 文本位置,则不会定义类型。在 在这种情况下,省略第二个参数; 将被调用 只有一个参数。@YYLTYPEyylex

如果您希望将其他参数传递给 ,请使用 just like (请参阅解析器函数 yyparse)。要将其他参数传递给 和 ,请使用 。yylex%lex-param%parse-paramyylexyyparse%param

指令: %lex-param {argument-declaration} ...

指定 that are additional argument 声明。您可以通过一个或多个此类声明,即 等同于重复。argument-declarationyylex%lex-param

指令: %param {argument-declaration} ...

指定附加 / 参数声明。这相当于 ‘’.您可以通过一项或多项 声明,这相当于重复.argument-declarationyylexyyparse%lex-param {argument-declaration} … %parse-param {argument-declaration} …%param

例如:

%lex-param   {scanner_mode *mode}
%parse-param {parser_mode *mode}
%param       {environment_type *env}

生成以下签名:

int yylex   (scanner_mode *mode, environment_type *env);
int yyparse (parser_mode *mode, environment_type *env);

如果添加了 '':%define api.pure full

int yylex   (YYSTYPE *lvalp, scanner_mode *mode, environment_type *env);
int yyparse (parser_mode *mode, environment_type *env);

最后,如果 '' 和 是 使用:%define api.pure full%locations

int yylex   (YYSTYPE *lvalp, YYLTYPE *llocp,
             scanner_mode *mode, environment_type *env);
int yyparse (parser_mode *mode, environment_type *env);

4.4 错误报告

在执行过程中,解析器可能会有错误消息要传递给用户, 例如语法错误或内存耗尽。如何传递此消息 必须由开发人员指定给用户。


4.4.1 错误报告功能yyerror

Bison 解析器检测到语法错误(或解析错误) 每当它读取不能满足任何语法规则的标记时。一 语法中的 action 也可以显式声明错误,使用 宏(请参阅在操作中使用的特殊功能)。YYERROR

Bison 解析器希望通过调用错误来报告错误 必须提供名为 的报告函数。是的 每当发现语法错误时调用,并且它 接收一个参数。对于语法错误,字符串通常为 .yyerroryyparse"syntax error"

如果在 Bison 声明部分(参见 Bison 声明部分),然后 Bison 提供 一个更详细和具体的错误消息字符串,而不仅仅是普通的 .但是,该消息有时包含 如果未启用 LAC,则信息不正确(请参阅 LAC)。%define parse.error detailedcustom"syntax error"

解析器可以检测到另一种错误:内存耗尽。这 当输入包含非常深的结构时,可能会发生这种情况 嵌 套。你不太可能遇到这种情况,因为野牛 解析器通常会自动将其堆栈扩展到一个非常大的限制。但 如果内存耗尽,则通常调用 fashion,但参数字符串是 。yyparseyyerror"memory exhausted"

在某些情况下,诊断是 之前自动从英语翻译成其他语言 它们被传递给 .请参阅 Parser 国际化"syntax error"yyerror

以下定义在简单程序中就足够了:

void
yyerror (char const *s)
{
  fprintf (stderr, "%s\n", s);
}

返回后,后者将尝试 错误恢复(如果您编写了合适的错误恢复语法规则) (请参阅错误恢复)。如果无法恢复,将 立即返回 1。yyerroryyparseyyparse

显然,在位置跟踪纯解析器中,应该有 对当前位置的访问。有了 ,这是 GLR 解析器确实如此,但 Yacc 解析器并非如此,因为 历史原因,这就是原因 首选。yyerror%define api.pure%define api.pure full%define api.pure

使用时,具有 以下签名:%locations %define api.pure fullyyerror

void yyerror (YYLTYPE *locp, char const *msg);

原型只是 Bison 生成代码的指示 使用。Bison 生成的代码总是忽略返回的 值,因此可以返回任何类型,包括 . 此外,可以是可变函数;这就是为什么 消息始终最后传递。yyerroryyerrorvoidyyerror

传统上返回一个 总是 被忽略了,但这纯粹是出于历史原因,而且是 最好,因为它更准确地描述了 的返回类型。yyerrorintvoidyyerror

该变量包含语法错误的数量 到目前为止报道。通常,此变量是全局变量;但如果你 请求纯解析器(请参阅纯(重入)解析器) 那么它是一个局部变量,只有操作才能访问。yynerrs


上一篇: , 上一篇: 上报错误 [内容][索引]

4.4.2 语法错误报告功能yyreport_syntax_error

如果调用 ''(请参阅 Bison 声明部分),则解析器不再将语法错误消息传递给 ,而是通过调用函数将该任务委托给用户。%define parse.error customyyerroryyreport_syntax_error

以下函数和类型是 “”: 它们在 实现文件 () 并且只能从那里获得。他们 旨在从语法的结语中使用。static*.c

功能:static int yyreport_syntax_error const yypcontext_t *ctx

向用户报告语法错误。成功时返回 0,开 记忆力耗尽。是否使用取决于用户。YYENOMEMyyerror

使用以下类型和函数生成错误消息。

类型:yypcontext_t

捕获语法错误情况的不透明类型。

类型:yysymbol_kind_t

所有语法符号、标记和非终端的枚举。其 枚举器是从符号名称伪造的:

enum yysymbol_kind_t
{
  YYSYMBOL_YYEMPTY = -2,      /* No symbol.  */
  YYSYMBOL_YYEOF = 0,         /* "end of file"  */
  YYSYMBOL_YYerror = 1,       /* error  */
  YYSYMBOL_YYUNDEF = 2,       /* "invalid token"  */
  YYSYMBOL_PLUS = 3,          /* "+"  */
  YYSYMBOL_MINUS = 4,         /* "-"  */
  [...]
  YYSYMBOL_VAR = 14,          /* "variable"  */
  YYSYMBOL_NEG = 15,          /* NEG  */
  YYSYMBOL_YYACCEPT = 16,     /* $accept  */
  YYSYMBOL_exp = 17,          /* exp  */
  YYSYMBOL_input = 18         /* input  */
};
typedef enum yysymbol_kind_t yysymbol_kind_t;
功能:静态yysymbol_kind_t yypcontext_tokenconst yypcontext_t *ctx

“意外”令牌:导致 语法错误。如果没有前瞻,则返回。YYSYMBOL_YYEMPTY

功能:静态 YYLTYPE * yypcontext_location const yypcontext_t *ctx

语法错误的位置(意外标记的位置)。

功能: static int yypcontext_expected_tokens const yypcontext_t *ctx, yysymbol_kind_t argv[]int argc

填充预期的标记,其中从不包含 、 或 。argvYYSYMBOL_YYEMPTYYYSYMBOL_YYerrorYYSYMBOL_YYUNDEF

永远不要把更多的元素放进去,在成功上 返回存储在 中的令牌数。如果还有更多 预期的令牌比 ,填满并返回 0. 如果没有预期的令牌,则也返回 0,但设置为 。argcargvargvargcargvargcargv[0]YYSYMBOL_YYEMPTY

启用 LAC 后,可能会在错误时返回负数, 例如内存耗尽。YYENOMEM

如果为 null,则返回存储所有可能文件所需的大小 值,它总是小于 。argvYYNTOKENS

功能:静态常量 char * yysymbol_namesymbol_kind_t符号

其种类为 的符号的名称,可能已翻译。symbol

自定义语法错误函数如下所示。此实现是 不适合国际化,请参阅示例以获取更好的替代方案。c/bistromathic

static int
yyreport_syntax_error (const yypcontext_t *ctx)
{
  int res = 0;
  YYLOCATION_PRINT (stderr, *yypcontext_location (ctx));
  fprintf (stderr, ": syntax error");
  // Report the tokens expected at this point.
  {
    enum { TOKENMAX = 5 };
    yysymbol_kind_t expected[TOKENMAX];
    int n = yypcontext_expected_tokens (ctx, expected, TOKENMAX);
    if (n < 0)
      // Forward errors to yyparse.
      res = n;
    else
      for (int i = 0; i < n; ++i)
        fprintf (stderr, "%s %s",
                 i == 0 ? ": expected" : " or", yysymbol_name (expected[i]));
  }
  // Report the unexpected token.
  {
    yysymbol_kind_t lookahead = yypcontext_token (ctx);
    if (lookahead != YYSYMBOL_YYEMPTY)
      fprintf (stderr, " before %s", yysymbol_name (lookahead));
  }
  fprintf (stderr, "\n");
  return res;
}

您仍然必须提供一个函数,例如用于 报告内存耗尽。yyerror


Next: , 上一篇: , 上一篇: 解析器C语言接口[内容][索引]

4.5 在操作中使用的特殊功能

下面是一个 Bison 结构、变量和宏的表格,它们在 行动。

变量:$$

就像一个变量,其中包含 按当前规则进行分组。请参阅操作

变量:$n

就像一个变量,它包含当前规则的第 th 个组件的语义值。请参阅操作n

变量:$<typealt>$

喜欢但在联合中指定替代项 由声明指定。请参阅操作中值的数据类型$$typealt%union

变量:$<typealt>n

喜欢但指定替代项 声明指定的联合。 请参阅操作中值的数据类型$ntypealt%union

宏:YYABORT ;

立即从 返回,指示失败。 请参阅 Parser 函数 yyparseyyparse

宏:YYACCEPT ;

立即返回 ,表示成功。 请参阅 Parser 函数 yyparseyyparse

宏:YYBACKUP令牌);

取消移动令牌。此宏仅允许用于减少 单个值,并且仅当没有 Lookahead 令牌时。 在 GLR 解析器中也不允许这样做。 它安装一个具有令牌种类和 语义价值 ;然后它丢弃了 将因此规则而减少。tokenvalue

如果宏在无效时使用,例如当有 已经是 Lookahead 令牌,然后它报告语法错误 消息 '' 并执行普通错误 恢复。cannot back up

无论哪种情况,都不会执行操作的其余部分。

值:YYEMPTY

没有 lookahead 令牌时存储的值。yychar

值:YYEOF

当前瞻是输入结束时存储的值 流。yychar

宏:YYERROR;

立即导致语法错误。此语句引发错误 恢复,就好像解析器本身检测到错误一样;但是,它 不调用 ,也不打印任何消息。如果你 想要打印错误消息,请在之前显式调用 '' 语句。请参阅错误恢复yyerroryyerrorYYERROR;

宏:YYNOMEM ;

立即从 返回,表示内存耗尽。 请参阅 Parser 函数 yyparseyyparse

宏:YYRECOVERING

当解析器生成 1 时,表达式生成 1 正在从语法错误中恢复,否则为 0。 请参阅错误恢复YYRECOVERING ()

变量:yychar

包含 lookahead 令牌的变量,或者当 Lookahead 是输入流的结束,或者当没有 lookahead 时 已执行,因此下一个令牌尚不清楚。 不要在延迟语义操作中进行修改(请参阅 GLR 语义操作)。 请参阅 Lookahead 令牌YYEOFYYEMPTYyychar

宏:yyclearin ;

丢弃当前前瞻令牌。这主要用于 错误规则。 不要在延迟的语义操作中调用(请参阅 GLR 语义操作)。 请参阅错误恢复yyclearin

宏: yyerrok ;

立即恢复为后续语法生成错误消息 错误。这主要在错误规则中有用。 请参阅错误恢复

变量:yylloc

未设置包含 lookahead 令牌位置的变量 到 或 . 不要在延迟语义操作中进行修改(请参阅 GLR 语义操作)。 请参阅操作和位置yycharYYEMPTYYYEOFyylloc

变量:yylval

包含 lookahead 标记语义值的变量 when 未设置为 或 。 不要在延迟语义操作中进行修改(请参阅 GLR 语义操作)。 请参阅操作yycharYYEMPTYYYEOFyylval

价值:@$

就像一个结构变量,包含有关文本的信息 当前规则创建的分组的位置。请参阅追踪位置

值:@n

就像一个结构变量,包含有关文本的信息 当前规则的第 th 个组件的位置。请参阅追踪位置n


4.6 解析器国际化

Bison 生成的解析器可以打印诊断信息,包括错误和 跟踪消息。默认情况下,它们以英语显示。然而,野牛 还支持以用户的母语输出诊断。自 使此工作,用户应设置通常的环境变量。 参见 GNU gettext 实用程序中的用户视图。 例如,shell 命令 '' might 使用 UTF-8 将用户的区域设置设置为加拿大法语 编码。可用区域设置的确切集取决于用户的 安装。export LC_ALL=fr_CA.UTF-8


下一篇: , 上一篇: 解析器国际化 [内容][首页]

4.6.1 实现国际化

使用 Bison 生成的解析器的包的维护者启用 通过以下方式实现解析器输出的国际化 步骤。在这里,我们假设一个使用 GNU Autoconf 和 GNU 自动生成。

  1. 进入包含所用 GNU Autoconf 宏的目录 通过包(通常称为)复制 Bison 安装的文件 '' 在 Bison 的安装目录中。 例如:m4bison-i18n.m4share/aclocal/bison-i18n.m4
    cp /usr/local/share/aclocal/bison-i18n.m4 m4/bison-i18n.m4
    
  2. 在顶级 中,在调用之后添加 的调用。这个宏是 在之前复制的文件中定义。它 导致找到变量的值,并定义源语言 符号在 Bison 生成的解析器。configure.acAM_GNU_GETTEXTBISON_I18Nbison-i18n.m4configureBISON_LOCALEDIRYYENABLE_NLS
  3. 在程序的功能中,指定目录 包含 Bison 的运行时消息目录,通过调用 '' 的域名为 ''。 例如:mainbindtextdomainbison-runtime
    bindtextdomain ("bison-runtime", BISON_LOCALEDIR);
    

    通常,这会显示在包已有的任何其他调用之后。在这里,我们依靠 '' 定义为通过 .bindtextdomain (PACKAGE, LOCALEDIR)BISON_LOCALEDIRMakefile

  4. 在控制函数编译的 中,使 '' 可用作 C 预处理器宏, 在“”或“”中。例如:Makefile.ammainBISON_LOCALEDIRDEFSAM_CPPFLAGS
    DEFS = @DEFS@ -DBISON_LOCALEDIR='"$(BISON_LOCALEDIR)"'
    

    或:

    AM_CPPFLAGS = -DBISON_LOCALEDIR='"$(BISON_LOCALEDIR)"'
    
  5. 最后,调用命令生成生成 基础设施。autoreconf

上一篇: , 上一篇: 解析器国际化 [内容][索引]

4.6.2 代币国际化

当变量设置为 或 时,可以对令牌别名进行国际化:%defineparse.errorcustomdetailed

%token
    '\n'   _("end of line")
  <double>
    NUM    _("number")
  <symrec*>
    FUN    _("function")
    VAR    _("variable")

语法的其余部分可以自由使用标记符号 () 或其别名 (),但不使用 国际化标记 ()。FUN"function"_("function")

如果至少有一个令牌别名是国际化的,则生成的解析器 将同时使用 和 ,必须定义 (参见 GNU gettext 实用程序中的程序员视图)。它们仅用于标记为要翻译的字符串别名。 换言之,即使您的目录具有 “function”,则使用N__

%token
  <symrec*>
    FUN      "function"
    VAR    _("variable")

“function”将在调试跟踪和错误消息中显示为未翻译。

除非由用户定义,否则将提供文件末标记 , “end of file”作为别名。如果用户 国际化代币。若要将其映射到另一个字符串,请使用:YYEOF

%token END 0 _("end of input")

Next: , Previous: , 上一篇: Bison [Contents][Index]

5 Bison 解析器算法

当 Bison 读取令牌时,它会将它们与它们的 语义值。该堆栈称为解析器堆栈。推送 令牌传统上称为移位

例如,假设中缀计算器的读数为 '',其中 ''来了。堆栈将有四个元素,每个令牌一个 这被转移了。1 + 5 *3

但是,堆栈并不总是具有每个读取的令牌的元素。什么时候 最后移动的标记和分组与 语法规则,它们可以根据该规则进行组合。这称为还原。这些令牌和分组在堆栈上被替换为 其符号是该规则的结果(左侧)的单个分组。 运行规则的操作是缩减过程的一部分,因为这 用于计算生成的分组的语义值。n

例如,如果中缀计算器的解析器堆栈包含以下内容:

1 + 5 * 3

下一个输入标记是换行符,然后是最后三个 元素可以通过以下规则减少到 15 个:

expr: expr '*' expr;

然后,堆栈仅包含以下三个元素:

1 + 15

此时,可以再进行一次减少,从而产生单个值 16. 然后可以移动换行符。

解析器尝试通过移位和缩减来减少整个输入 到其符号是语法的起始符号的单个分组 (请参阅语言和上下文无关语法)。

这种解析器在文献中被称为自下而上的解析器。


5.1 Lookahead 代币

Bison 解析器并不总是在 最后的标记和分组与规则匹配。这是因为这样的 简单的策略不足以处理大多数语言。相反,当 减少是可能的,解析器有时会“向前看”下一个 令牌,以便决定做什么。n

读取令牌时,它不会立即移动;首先,它成为 Lookahead 令牌,它不在堆栈上。现在解析器可以 在堆栈上执行一个或多个标记和分组的缩减,而 Lookahead 令牌仍然偏向一边。当不再减少时 如果发生,则 Lookahead 令牌将转移到堆栈上。这 并不意味着所有可能的削减都已完成;取决于 token 种类的 lookahead token,某些规则可能会选择延迟其 应用。

这是一个需要提前看的简单案例。这三条规则定义了 包含二进制加法运算符和后缀一元的表达式 阶乘运算符 (''),并允许使用括号进行分组。!

expr:
  term '+' expr
| term
;
term:
  '(' expr ')'
| term '!'
| "number"
;

假设标记 '' 已被读取和移动;什么 应该做吗?如果以下标记是 '',则前三个 标记必须简化以形成 .这是唯一有效的 当然,因为移动“”会产生一系列符号,并且没有规则允许这样做。1 + 2)expr)term ')'

如果以下标记是 '',则必须立即将其移位,以便 该 '' 可以简化为 .如果相反, 解析器在移位之前要减少,'' 将变成 .这样就不可能移动“”,因为 这样做会在堆栈上生成符号序列。没有规则允许该序列。!2 !term1 + 2expr!expr '!'

lookahead 令牌存储在变量 中。它的语义 value 和 location(如果有)存储在变量 和 中。请参阅在操作中使用的特殊功能yycharyylvalyylloc


下一篇: , 上一篇: , 上一篇: Bison Parser 算法 [内容][索引]

5.2 转移/减少冲突

假设我们正在解析一种具有 if-then 和 if-then-else 的语言 语句,具有一对如下规则:

if_stmt:
  "if" expr "then" stmt
| "if" expr "then" stmt "else" stmt
;

这里 和 是终端符号 特定关键字标记。"if""then""else"

当令牌被读取并成为前瞻令牌时, 堆栈的内容(假设输入有效)正好适合 根据第一条规则减少。但移动 也是合理的,因为这将导致最终减少秒 统治。"else""else"

这种情况,无论是轮班还是裁员都是有效的,是 称为 shift/reduce 冲突。Bison 旨在解决 这些冲突通过选择转移,除非另有指示 运算符优先级声明。要了解其中的原因,让我们 将其与其他替代方案进行对比。

由于解析器更喜欢移动 ,因此结果是附加 最里面的 if 语句的 else-子句,使这两个输入 等效:"else"

if x then if y then win; else lose;

if x then do; if y then win; else lose; end;

但是,如果解析器选择尽可能减少而不是移动,则 结果是将 else-子句附加到最外层的 if 语句, 使这两个输入等效:

if x then if y then win; else lose;

if x then do; if y then win; end; else lose;

冲突之所以存在,是因为所写的语法是模棱两可的:要么 解析简单的嵌套 if 语句是合法的。已建立的 按照惯例,这些歧义可以通过附加 else-子句添加到最里面的 if 语句;这就是野牛所取得的成就 通过选择转移而不是减少。(理想情况下,它会更干净 写一个明确的语法,但在这种情况下很难做到。 这种特殊的歧义首先出现在 Algol 60 被称为“悬空”歧义。else

为了帮助语法作者理解每个冲突的本质, 可以要求野牛生成“反例”。在本案中,它 实际上甚至通过展示字符串来证明语法是模棱两可的 具有两种不同的解析:

  Example: "if" expr "then" "if" expr "then" stmt  "else" stmt
  Shift derivation
    if_stmt
    ↳ 3: "if" expr "then" stmt
                           ↳ 2: if_stmt
                                 ↳ 4: "if" expr "then" stmt  "else" stmt
  Example: "if" expr "then" "if" expr "then" stmt  "else" stmt
  Reduce derivation
    if_stmt
    ↳ 4: "if" expr "then" stmt                                "else" stmt
                           ↳ 2: if_stmt
                                 ↳ 3: "if" expr "then" stmt 

有关详细信息,请参阅反例的生成


为了避免 Bison 发出关于可预测的、合法的移位/减少的警告 冲突时,可以使用声明。 只要 shift/reduce 冲突的数量,就不会有警告 正好是 ,如果出现 不同的数字。 请参阅禁止显示冲突警告。但是,我们没有 建议使用 (除了 ''!),作为 冲突的数量并不意味着它们是相同的。什么时候 可能,您应该使用 precedence 指令来修复 显式冲突(请参阅对非运算符使用优先级)。%expect nn%expect%expect 0

上述定义完全归咎于 冲突,但如果没有额外的冲突,冲突实际上不会出现 规则。这是一个完整的 Bison 语法文件,实际表现出来 冲突:if_stmt

%%
stmt:
  expr
| if_stmt
;
if_stmt:
  "if" expr "then" stmt
| "if" expr "then" stmt "else" stmt
;
expr:
  "identifier"
;

5.3 运算符优先级

出现移位/减少冲突的另一种情况是算术 表达 式。在这里,移位并不总是首选的解决方案;这 运算符优先级的 Bison 声明允许您指定何时 转变和何时减少。


5.3.1 何时需要优先

考虑以下模棱两可的语法片段(模棱两可,因为 输入 '' 可以通过两种不同的方式进行解析):- 2 * 3

expr:
  expr '-' expr
| expr '*' expr
| expr '<' expr
| '(' expr ')'
…
;

假设解析器已经看到了标记 ''、'' 和 ''; 它应该通过减法运算符的规则来减少它们吗?它 取决于下一个令牌。当然,如果下一个标记是“”,我们 必须减少;移位是无效的,因为没有一个规则可以减少 令牌序列 '' 或任何以该序列开头的内容。但是,如果 下一个标记是 '' 或 '',我们有一个选择:要么 移位或减少将允许解析完成,但 不同的结果。1-2)- 2 )*<

要决定野牛应该做哪一个,我们必须考虑结果。如果 下一个运算符令牌被移动,然后必须减少它 首先,为了允许另一个机会来减少差异。 结果是(实际上)''。另一方面 手,如果减法在移位前减少,结果 是 ''。显然,选择换档或 reduce 应取决于运算符的相对优先级 '' 和 : '' 应该首先移动,但不要 ‘’.op- (2 op 3)op(1 - 2) op 3-op*<

诸如“”之类的输入呢?这应该是 '' 还是应该是 ''?对于大多数人 运算符我们更喜欢前者,这称为左关联。 后一种选择,即正确的关联,是可取的 赋值运算符。左关联或右关联的选择是一个 解析器在堆栈时选择移位还是减少的问题 包含 '',并且 Lookahead 标记为 '': Moving 使右联想。- 2 - 5(1 - 2) - 5- (2 - 5)- 2-


下一篇: , 上一篇: , 向上: 运算符优先级 [内容][索引]

5.3.2 指定运算符优先级

Bison 允许您使用运算符优先级指定这些选项 声明和 .每个这样的声明 包含令牌列表,这些令牌是优先级和 正在宣布关联性。宣言使所有 那些运算符左关联,声明使 他们右联想。第三种选择是 ,它 声明在“在 行“。 最后一种选择,只允许定义 优先级,根本没有关联性。因此,任何 仍然存在的与关联相关的冲突将报告为 编译时错误。该指令创建运行时 错误:以关联方式使用运算符是语法错误。这 指令会创建编译时错误:运算符可能涉及与关联性相关的冲突,这与 语法作者的期望。%left%right%left%right%nonassoc%precedence%nonassoc%precedence

不同运算符的相对优先级由 声明它们的顺序。第一个优先级/关联性 文件中的声明声明其 优先级最低,下一个此类声明声明运算符 其优先级稍高,依此类推。


5.3.3 仅指定优先级

由于 POSIX Yacc 只定义了 、 和 ,它们都定义了优先级和关联性,因此很少 需要注意的是,没有 定义关联性。然而,有时,当试图解决 冲突,优先就足够了。在这种情况下,使用 、 或 可能隐藏未来 (关联性 相关)的冲突将保持隐藏状态。%left%right%nonassoc%left%right%nonassoc

可以解决悬而未决的歧义(参见 Shift/Reduce Conflicts) 明确地。此 shift/reduce 冲突发生在以下情况下: 其中句点表示当前解析状态:else

if e1 then if  e2 then s1 • else s2

冲突涉及规则 '' 的减少,默认情况下,该规则的优先级是其最后一个标记的优先级 (),以及令牌的移动。通常 消除歧义(将 附加到最接近的), 移位必须是首选,即 的优先级必须为 高于 。但预计两者都不会参与其中 在与关联性相关的冲突中,可以按如下方式指定。IF expr THEN stmtTHENELSEelseifELSETHEN

%precedence THEN
%precedence ELSE

一元减号是另一个典型的例子,其中关联性通常是 过度指定,请参阅 Infix Notation Calculator: calc。该指令是 传统上用于声明 的优先级,哪个更 比需要的要多,因为它还定义了它的关联性。虽然这是无害的 在传统示例中,谁知道将来会如何使用 语法的演变...%leftNEGNEG


5.3.4 优先级示例

在我们的示例中,我们需要以下声明:

%left '<'
%left '-'
%left '*'

在一个更完整的示例中,我们也支持其他运算符,我们 将以同等优先级的组宣布它们。例如,是 声明用:'+''-'

%left '<' '>' '=' "!=" "<=" ">="
%left '+' '-'
%left '*' '/'

5.3.5 优先级如何工作

优先级声明的第一个效果是分配优先级 级别到声明的终端符号。第二个效果是分配 特定规则的优先级:每个规则的优先级来自 组件中提到的最后一个终端符号。(您也可以 显式指定规则的优先级。请参阅上下文相关优先级

最后,通过比较优先级来解决冲突 的规则与 lookahead 令牌的规则一起考虑。如果 代币的优先级更高,选择是转移。如果规则是 优先级更高,选择是减少。如果他们有相等的 优先级,选择是基于其关联性做出的 优先级。由(请参阅调用 Bison)制作的详细输出文件说明了每个冲突是如何发生的 解决。-v

并非所有规则和所有令牌都具有优先权。如果规则或 Lookahead 令牌没有优先级,则默认值为 Shift。


5.3.6 对非运算符使用优先级

正确使用优先级和关联性指令可以帮助修复 不涉及类似算术运算符的 shift/reduce 冲突。为 例如,“悬空”问题(请参阅移位/减少冲突)可以是 以两种不同的方式优雅地解决了。else

在本例中,冲突在于代币愿意 要转移,规则“”,询问 用于减少。默认情况下,规则的优先级是其最后一个规则的优先级 token,这里,所以冲突会得到适当的解决 通过给出高于 的优先级,对于 实例如下:"else"if_stmt: "if" expr "then" stmt"then""else""then"

%precedence "then"
%precedence "else"

或者,您可以为两个令牌提供相同的优先级,在这种情况下 关联性用于解决冲突。为了保留换档动作, 使用正确的关联性:

%right "then" "else"

然而,这两种解决方案都不是完美的。由于 Bison 目前不提供, “作用域”优先级,两者都强制你声明优先级 这些关键字相对于其他运算符的语法。 因此,您不会被告知新的冲突,而不是被警告 的(例如,由于“' 模棱两可:“或”“?),冲突将已经”修复“。if test then 1 else 2 + 3if test then 1 else (2 + 3)(if test then 1 else 2) + 3


下一篇: , 上一篇: , 上一篇: Bison 解析器算法 [内容][索引]

5.4 上下文相关优先级

通常,运算符的优先级取决于上下文。这听起来 乍一看很古怪,但确实很常见。例如,减号 符号通常具有非常高的一元运算符的优先级,并且 作为二进制运算符的优先级略低(低于乘法)。

Bison 优先声明 对于给定的令牌,只能使用一次;所以令牌有 以这种方式声明的只有一个优先级。对于上下文相关 优先级,您需要使用附加机制:规则修饰符。%prec

修饰符声明特定规则的优先级 指定一个终端符号,其优先级应用于该规则。 该符号不必以其他方式出现在规则中。这 修饰符的语法是:%prec

%prec terminal-symbol

它写在规则的组成部分之后。其作用是 为规则分配 的优先级 ,覆盖 以普通方式为它推导出的优先级。这 然后,更改的规则优先级会影响涉及该规则的冲突 已解析(请参阅运算符优先级)。terminal-symbol

以下是解决一元减号问题的方法。首先,声明 名为 的虚构终端符号的优先级。那里 不是这种类型的标记,但该符号用于代表其 优先:%precUMINUS

…
%left '+' '-'
%left '*'
%left UMINUS

现在,可以在特定规则中使用 的优先级:UMINUS

exp:
  …
| exp '-' exp
  …
| '-' exp %prec UMINUS

下一篇: , 上一篇: , 上一篇: Bison 解析器算法 [内容][索引]

5.5 解析器状态

该函数是使用有限状态机实现的。 在解析器堆栈上推送的值不仅仅是令牌种类代码;他们 表示终端和非终端符号的整个序列,位于 或 靠近堆栈的顶部。当前状态收集所有信息 关于与决定下一步做什么相关的先前输入。yyparse

每次读取 lookahead 令牌时,当前解析器状态以及 在表中查找 Lookahead 令牌的类型。此表条目可以 说,“移动前瞻令牌。在这种情况下,它还指定了新的 解析器状态,它被推送到解析器堆栈的顶部。或者它可以 说,“使用规则编号减少。这意味着一定数量的 的令牌或分组从堆栈的顶部移除,并替换为 一个分组。换言之,该数量的状态是从 堆栈,并推送一个新状态。n

还有另一种选择:该表可以说 lookahead 令牌 在当前状态下是错误的。这会导致错误处理开始 (请参阅错误恢复)。


下一篇: , 上一篇: , 上一篇: 野牛解析器算法 [内容][索引]

5.6 减少/减少冲突

如果有两个或多个适用的规则,则会发生减少/减少冲突 到相同的输入序列。这通常表示存在严重错误 在语法中。

例如,这里是定义序列的错误尝试 零个或多个分组。word

sequence:
  %empty         { printf ("empty sequence\n"); }
| maybeword
| sequence word  { printf ("added word %s\n", $2); }
;
maybeword:
  %empty    { printf ("empty maybeword\n"); }
| word      { printf ("single word %s\n", $1); }
;

错误是模棱两可的:正如反例生成所证明的那样 (请参阅反例的生成),有多种方法可以将单个解析为 .它可以通过第二条规则简化为 a,然后简化为 a。 或者,什么都不可以通过第一条规则简化为 a,这可以与使用 3 条规则结合使用 。wordsequencemaybewordsequencesequencewordsequence

还有不止一种方法可以将 nothing-at-all 简化为 .这可以通过第一条规则直接完成, 或间接通过,然后是第二条规则。sequencemaybeword

你可能会认为这是一个没有区别的区别,因为它 不会更改任何特定输入是否有效。但它确实如此 影响运行的操作。一个解析顺序运行第二个规则的 行动;另一个运行第一条规则的操作和第三条规则的操作。 在此示例中,程序的输出发生了变化。

Bison 通过选择使用以下规则来解决减少/减少冲突 在语法中首先出现,但依赖这个是非常危险的。每 必须研究减少/减少冲突,并且通常可以消除冲突。这是 正确的定义方式:sequence

sequence:
  %empty         { printf ("empty sequence\n"); }
| sequence word  { printf ("added word %s\n", $2); }
;

这是另一个导致 reduce/reduce 冲突的常见错误:

sequence:
  %empty
| sequence words
| sequence redirects
;
words:
  %empty
| words word
;
redirects:
  %empty
| redirects redirect
;

此处的目的是定义一个可以包含 or 分组的序列。和 的各个定义没有错误,但 三者结合在一起会产生微妙的歧义:即使是空输入也可以解析 以无限多种方式!wordredirectsequencewordsredirects

考虑一下:什么都不可能是.或者它可以是连续两个,或者三个,或任何数字。它同样可以是一个、两个或任何数字。或者它可以是 a 后面跟着 3 和 另一个 .等等。wordswordsredirectswordsredirectswords

以下是纠正这些规则的两种方法。首先,使其成为一个单一的级别 的序列:

sequence:
  %empty
| sequence word
| sequence redirect
;

其次,要防止 a 或 a 为空:wordsredirects

sequence:
  %empty
| sequence words
| sequence redirects
;
words:
  word
| words word
;
redirects:
  redirect
| redirects redirect
;

然而,这个提议引入了另一种模棱两可!输入 '' 可以解析为由两个组成的单个 ''s,或作为两个 one-(同样用于 /)。然而,这种模棱两可现在是一个 转移/减少冲突,因此现在可以优先解决它 指令。word wordwordswordwordwordsredirectredirects

为了简化问题,我们将继续使用和成为令牌:和。wordredirect"word""redirect"

要优先选择最长的,必须解决令牌和规则 '' 之间的冲突 作为转变。为此,我们使用与上面相同的技术,请参阅对非运算符使用优先级。一个解决方案 依赖于优先级:用于为 统治:words"word"sequence: sequence words%prec

%precedence "word"
%precedence "sequence"
%%
sequence:
  %empty
| sequence word      %prec "sequence"
| sequence redirect  %prec "sequence"
;
words:
  word
| words "word"
;

另一种解决方案依赖于关联性:同时提供令牌和 使用相同的优先级进行规则,但使它们具有正确的关联性:

%right "word" "redirect"
%%
sequence:
  %empty
| sequence word      %prec "word"
| sequence redirect  %prec "redirect"
;

下一篇: , 上一篇: , 上一篇: 野牛解析器算法 [内容][索引]

5.7 神秘的冲突

有时,可能会发生看起来没有必要的减少/减少冲突。 下面是一个示例:

%%
def: param_spec return_spec ',';
param_spec:
  type
| name_list ':' type
;
return_spec:
  type
| name ':' type
;
type: "id";

name: "id";
name_list:
  name
| name ',' name_list
;

似乎只能用一个标记来解析这种语法 前瞻:读取 A 时,如果后面跟着逗号或冒号,则 A 为 A,如果后面跟着另一个逗号或冒号,则为 A。换句话说,这个语法就是 LR(1)。然而野牛 找到一个 reduce/reduce 冲突,为此生成反例 (见反例的生成)会找到一个不统一的例子。param_spec"id"nametype"id"

这是因为 Bison 默认不处理所有 LR(1) 语法, 由于历史原因。 在这种语法中,有两个上下文,即在开头的 an 之后 的 a 和 同样在 a 的开头 ,它们非常相似,以至于 Bison 认为它们是 相同。 它们看起来很相似,因为同一组规则是 active—减少到 a 的规则和减少到 一个。Bison无法在处理的那个阶段确定 规则将要求在两者中使用不同的前瞻令牌 contexts,因此它为它们都创建了一个解析器状态。结合 这两个上下文稍后会导致冲突。在解析器术语中,这 occurrence 表示语法不是 LALR(1)。"id"param_specreturn_specnametype

对于许多实用语法(特别是那些属于非 LR 的语法)(1) 类),LALR(1)的局限性导致了不仅仅是 神秘的减少/减少冲突。解决所有这些问题的最佳方法 是选择不同的解析器表构造算法。也 IELR(1) 或规范 LR(1) 就足够了,但前者更有效 并且在开发过程中更易于调试。请参阅 LR 表构造,了解 详。

如果您希望绕过 LALR(1) 的局限性,则可以解决 通常可以通过识别两个解析器状态来解决神秘的冲突 被混淆了,并添加了一些东西来使它们看起来 不同。在上面的示例中,将一个规则添加到如下所示可使问题消失:return_spec

…
return_spec:
  type
| name ':' type
| "id" "bogus"       /* This rule is never used. */
;

这纠正了这个问题,因为它引入了 在 开头的上下文中的其他活动规则。此规则在相应的上下文中未处于活动状态 在 中,因此两个上下文接收不同的解析器状态。 只要令牌从来不是由 生成的 , 添加的规则无法更改实际输入的解析方式。"id"return_specparam_spec"bogus"yylex

在这个特定示例中,还有另一种解决问题的方法: 重写规则以直接使用 而不是通过 .这也导致两者混淆 上下文具有不同的活动规则集,因为 的 激活更改的规则 而不是 的 。return_spec"id"namereturn_specreturn_specname

param_spec:
  type
| name_list ':' type
;
return_spec:
  type
| "id" ':' type
;

有关 LALR(1) 解析器和解析器生成器的更详细阐述,请参见 DeRemer 1982


下一篇: , 上一篇: , 上一篇: Bison 解析器算法 [内容][索引]

5.8 调整 LR

Bison 基于 LR 的解析器的默认行为主要用于 历史原因,但这种行为往往不稳健。例如,在 在上一节中,我们讨论了可能存在的神秘冲突 由 LALR(1) 生成,这是 Bison 的默认解析器表构造算法。 另一个例子是 Bison 的指令, 它指示生成的解析器产生详细的语法错误 消息,有时可能包含不正确的信息。%define parse.error verbose

在本节中,我们将探讨 Bison 的几个现代功能,这些功能可让您 调整生成的基于 LR 的解析器的基本方面。一些 这些功能可以轻松消除上述缺点。 其他的可能纯粹有助于理解您的解析器。


下一篇: ,向上:调整 LR [内容][索引]

5.8.1 LR表结构

由于历史原因,Bison 默认构造 LALR(1) 解析器表。 但是,LALR 不具备 LR 的全部语言识别能力。 因此,使用 LALR 解析器表的解析器的行为通常 神秘。我们在《神秘的冲突》中展示了这种效应的简单例子。

正如我们在该示例中还演示的那样,传统方法 消除这种神秘的行为就是重组语法。 不幸的是,正确地做到这一点通常很困难。此外,仅仅 发现 LALR 在解析器中导致神秘行为可能是 也很难。

幸运的是,Bison提供了一种简单的方法来消除这种可能性 完全是神秘的行为。你只需要激活一个更强大的 使用指令的解析器表构造算法。%define lr.type

指令:%define lr.type type

指定 LR(1) 系列中解析器表的类型。接受的 的值为:type

  • lalr(默认)
  • ielr
  • canonical-lr

例如,若要激活 IELR,可以向 语法文件:

%define lr.type ielr

例如,在《神秘的冲突》中,神秘的 然后消除冲突,因此无需投入时间 理解冲突或重组语法以修复它。如果 在未来的发展过程中,语法的发展使得所有神秘的 仅使用 LALR 的行为就会消失,您不必担心 继续使用 IELR 将导致不必要的大型解析器表。 也就是说,IELR 在 LALR 时生成 LALR 表(使用确定性解析 algorithm)足以支持 LR。因此,通过在语法开发开始时启用 IELR,您可以 安全、彻底地消除了考虑 LALR 缺点的需要。

虽然 IELR 几乎总是更可取的,但在某些情况下,LALR 或者 Knuth 描述的规范 LR 解析器表(参见 Knuth 1965)可以 有用。在这里,我们总结了每个解析器表的相对优势 Bison中的构造算法:

  • 拉尔

    至少在两种情况下,LALR 是值得的:

    • 没有静态冲突解决的 GLR。

      使用 GLR 解析器时(请参阅编写 GLR 解析器),如果不解析任何 静态冲突(例如,与 或 冲突), 然后 解析器探索任何给定输入的所有潜在解析。在这种情况下, 解析器表构造算法的选择保证不会改变 解析器接受的语言。LALR 解析器表是最小的 Bison 目前可以构建的解析器表,因此它们可能更可取。 然而,一旦你开始静态地解决冲突,GLR就会表现得像 更像是句法上下文中的确定性解析器,其中那些 出现冲突,因此 IELR 或规范 LR 都有助于 避免LALR的神秘行为。%left%precedence

    • 语法格式错误。

      在开发过程中,偶尔会出现一种特别畸形的语法,带有 反复出现的主要缺陷可能会严重阻碍 IELR 或规范 LR 解析器 表构造算法。LALR 可以成为构建解析器的快速方法 表,以便调查此类问题,同时忽略更微妙的问题 与 IELR 和规范 LR 的区别。

  • 国际娱乐评论

    IELR(不足消除 LR)是一种最小的 LR 算法。也就是说,给定 任何语法(LR 或非 LR)、使用 IELR 或规范 LR 解析器表的解析器 始终接受完全相同的句子集。但是,与 LALR 一样,IELR 在解析器表构造期间合并解析器状态,以便 解析器状态通常比规范 LR 小一个数量级。 更重要的是,因为规范 LR 的额外解析器状态可能包含 在非 LR 语法的情况下,重复冲突的数量 因为 IELR 通常也少一个数量级。这种效果可以 显著降低语法开发的复杂性。

  • 规范 LR

    虽然效率低下,但规范的 LR 解析器表可能是一种有趣的方法 探索语法,因为它们具有 IELR 和 LALR 表的属性 不要。也就是说,如果未使用,并且默认减少为 left disabled(请参阅默认减少),然后,对于每个左侧上下文 每个规范的 LR 状态,该状态接受的令牌集是 保证是语法上可接受的确切标记集 那左边的背景。这样看来,规范 LR 的一个优势 生产中的解析器是,在上述约束下,它们是 保证在不执行的情况下尽快检测到语法错误 任何不必要的减少。但是,使用 LAC 的 IELR 解析器也是 能够在不牺牲或 默认减少。有关 LAC 的详细信息和一些注意事项,请参阅 LAC。%nonassoc%nonassoc

更详细地阐述 LALR 解析器中的神秘行为 以及 IELR 的好处,参见 Denny 2008Denny 2010 November


下一篇: , 上一篇: , 上一篇: 调整LR [内容][索引]

5.8.2 默认减少

在构建解析器表后,Bison 使用 每个解析器状态中的最大前瞻集。要减小 解析器状态,传统的 Bison 行为是删除该 lookahead 集和 将该缩减指定为默认分析器操作。这样的减少 称为默认减少

默认缩减影响的不仅仅是分析器表的大小。他们 还会影响解析器的行为:

  • 延迟调用。yylex

    一致状态是只有一个可能的解析器的状态 行动。如果该操作是缩减并编码为默认值 还原,则该一致状态称为默认状态。 在达到默认状态后,Bison 生成的解析器不会打扰 invoke 在执行缩减之前获取下一个令牌。 换言之,是否在一致状态下启用默认减少 确定 Bison 生成的解析器调用 token:当它到达输入中的该令牌时立即或当它 最终需要该令牌作为前瞻,以确定下一个 解析器操作。传统上,默认减少是启用的,因此 Parser 表现出后一种行为。yylexyylex

    在以下情况下,默认状态的存在是一个重要的考虑因素 设计和语法文件。也就是说,如果行为可以影响或受语义行为的影响 与默认状态的减少相关联,然后是 在这些减少之后的下一次调用非常重要。 例如,语义操作可能会弹出一个作用域堆栈,用于确定要返回的令牌。因此,延迟可能是必要的 以确保不会在作用域中查找下一个令牌 应该已经被视为已关闭。yylexyylexyylexyylexyylex

  • 语法错误检测延迟。

    当解析器通过调用 获取新令牌时,它会检查 在当前解析器状态下是否存在针对该令牌的操作。这 解析器检测到语法错误当且仅当 (1) 没有操作 对于该令牌或 (2) 该令牌的操作是错误操作(由于 的使用)。但是,如果默认减少 该状态(可能是也可能不是默认状态),那么它是 条件 1 不可能存在。也就是说,所有令牌都有一个操作。 因此,解析器有时无法检测到语法错误,直到它达到 后来的状态。yylex%nonassoc

    虽然默认减少永远不会导致解析器在语法上接受 不正确的句子,语法错误检测的延迟可能会有意想不到的 对解析器行为的影响。但是,可能会导致延迟 无论如何,通过解析器状态合并和使用 ,它可以 由另一个 Bison 功能 LAC 修复。我们讨论延迟的影响 语法错误检测和 LAC 将在下一节中详细介绍(请参阅 LAC)。%nonassoc

对于规范 LR,Bison 默认启用的唯一默认减少 是 accept 操作,它只出现在 Accept 状态中,它具有 没有其他操作,因此是默认状态。但是,默认接受 操作不会延迟任何调用或语法错误检测 因为 Accept 操作结束分析。yylex

对于 LALR 和 IELR,Bison 在几乎所有州都通过以下方式实现默认减少 违约。只有两个例外。首先,有转变的州 对令牌的操作没有默认减少,因为 延迟的语法错误检测可能会阻止令牌 从在那种状态下被转移。但是,解析器状态合并可以 无论如何都会引起相同的效果,并且 LAC 在这两种情况下都会修复它,所以将来 当 LAC 被激活时,Bison 的版本可能会删除此异常。第二 GLR 解析器不会将默认减少记录为前瞻操作 存在冲突的令牌。在这种情况下,正确的操作是 而是拆分解析。errorerror

要调整哪些州启用了默认减少,请使用指令。%define lr.default-reduction

指令:%define lr.default-reduction 其中

指定允许包含缺省减少的状态类型。 可接受的值为:where

  • most(LALR 和 IELR 的默认值)
  • consistent
  • accepting(规范 LR 的默认值)

下一篇: , 上一篇: , 向上: 调整 LR [内容][索引]

5.8.3 乳腺

Canonical LR、IELR 和 LALR 可能会遇到一些问题 遇到语法错误。首先,解析器可能会执行其他 在发现语法错误之前减少解析器堆栈。这样 缩减可以执行意外的用户语义操作,因为 它们基于无效的令牌,并导致错误恢复开始 在与无效标记所在的语法上下文不同的语法上下文中 遇到。其次,当启用详细错误消息时(请参阅错误报告),语法错误消息中的预期令牌列表可以同时 包含无效令牌并省略有效令牌。

造成上述问题的罪魁祸首是,默认减少 处于不一致状态(请参阅默认减少)和解析器状态 合并。由于 IELR 和 LALR 合并解析器状态,因此它们受到的影响最大。 Canonical LR 只有在使用或默认时才会受到影响 对于不一致的状态,将启用减少。%nonassoc%nonassoc

LAC(Lookahead Correction)是解析算法中的一种新机制 这解决了规范 LR、IELR 和 LALR 的这些问题,而无需 牺牲、默认减少或状态合并。您可以 使用指令启用 LAC。%nonassoc%define parse.lac

指令:%define parse.lac

启用 LAC 以改进语法错误处理。

  • none(默认)
  • full

此功能目前仅适用于 C 和 C++ 中的确定性解析器。

从概念上讲,LAC 机制是直截了当的。每当解析器 从扫描程序获取新令牌,以便它可以确定下一个 解析器操作,它会立即暂停正常解析并执行 使用普通解析器状态堆栈的临时副本进行探索性解析。 在此探索性分析期间,分析器不执行用户语义 行动。如果探索性解析达到移位操作,则正常解析 然后在普通解析器堆栈上恢复。如果探索性分析达到 错误 相反,分析器会报告语法错误。如果 verbose 语法 错误消息启用后,解析器必须发现 预期的令牌,因此它对每个令牌执行单独的探索性分析 在语法中。

LAC的使用有一个微妙之处。也就是说,当在一致的 具有默认约简的解析器状态,解析器不会尝试获取 来自扫描程序的令牌,因为不需要提前查看来确定 下一个分析器操作。因此,是否在 一致状态(请参阅默认减少)会影响分析器的速度 检测语法错误:当它到达错误时立即 token,或者当它最终需要该令牌作为前瞻时 确定下一个分析器操作。后一种行为可能更多 直觉,所以Bison目前没有提供实现前一种行为的方法 而默认减少在一致状态下启用。

因此,当 LAC 在使用时,对于是否启用某些固定决定 默认减少 在一致状态下,规范 LR 和 IELR 的行为几乎 句法上可接受和句法上完全相同 不可接受的输入。虽然 LALR 仍然不支持完整的 规范 LR 和 IELR 的语言识别能力,LAC 至少能够实现 LALR 的语法错误处理,以正确反映 LALR 的 语言识别能力。

使用 LAC 时需要考虑一些注意事项:

  • 无限解析循环。

    IELR 加 LAC 相对于规范 LR 确实有一个缺点。一些 Bison 生成的解析器可以无限循环。LAC 不固定无限 分析在遇到语法错误和检测之间发生的循环 它,但有时会启用规范 LR 或禁用默认减少 确实如此。

  • 详细错误消息限制。

    出于国际化的考虑,Bison 生成的解析器 限制他们愿意在 详细语法错误消息。如果预期令牌的数量超过该数量 limit,则该列表只是从消息中删除。启用 LAC 可以 增加列表的大小,从而导致分析器删除它。之 当然,删除列表比报告不正确的列表要好。

  • 性能。

    由于 LAC 需要执行两次许多解析操作,因此它可以具有 绩效损失。但是,并非所有分析操作都必须执行 两次。具体来说,在一系列默认减少期间,一致性 状态和移位操作,解析器永远不必启动探索性 解析。此外,解析中最耗时的任务通常是 文件 I/O、扫描程序执行的词法分析以及用户的 语义操作,但这些都不是在探索期间执行的 解析。最后,在探索期间使用的临时堆栈的底部 parse 是指向普通解析器状态堆栈的指针,因此堆栈是 从未物理复制过。根据我们的经验,LAC 的性能损失 事实证明,对于实用语法来说,这是微不足道的。

虽然 LAC 算法共享已在 解析器社区多年来,有关介绍 LAC 的出版物,请参阅 Denny 2010 May


上一篇: 上一篇: 调整LR [内容][索引]

5.8.4 无法访问的状态

如果不存在从解析器的开始状态到 一些状态,那么野牛认为是无法到达的 状态。在解决冲突期间,如果 Bison 禁用导致该移位操作从前置状态。ss

默认情况下,Bison 会在冲突后从解析器中删除无法访问的状态 分辨率,因为它们在生成的解析器中是无用的。然而 在尝试理解 解析器和语法之间的关系。

指令:%define lr.keep-unreachable-state value

请求 Bison 允许无法访问的状态保留在解析器表中。 必须是布尔值。默认值为 。valuefalse

需要考虑以下几点:

  • 缺少警告或无关警告。

    无法访问的状态可能包含冲突,并且可能使用任何 其他状态。因此,保持不可访问状态可能会诱发以下警告: 与解析器的行为无关,并且可能会消除以下警告: 相关。当然,警告的变化实际上可能与 解析表分析想要保持无法访问的状态,所以这个 行为可能会在未来的 Bison 版本中保留。

  • 其他无用状态。

    虽然 Bison 能够删除无法访问的状态,但不能保证 删除其他类型的无用状态。具体来说,当 Bison 禁用 减少冲突解决过程中的操作,某些转到操作可能会变成 无用,因此一些额外的状态可能变得无用。如果野牛是 要计算哪些 goto 操作是无用的,然后禁用这些操作, 它可以将此类状态识别为无法访问,然后删除这些状态。 但是,Bison 不会计算哪些 goto 操作是无用的。


下一篇: , 上一篇: , 上一篇: Bison 解析器算法 [内容][索引]

5.9 广义 LR (GLR) 解析

Bison 生成唯一选择的确定性解析器 何时减少以及应用哪种减少 基于前面输入的摘要和一个额外的 Lookahead 标记。 因此,普通的野牛会处理 上下文无关的语言。 模棱两可的语法,因为它们具有多个可能的字符串 从这个意义上说,还原序列不能有确定性解析器。 对于需要多个符号的语言也是如此 展望未来,因为解析器缺乏制作 必须在 shift/reduce 解析器中做出决定。 最后,如前所述(见神秘冲突), 在某些语言中,Bison 默认选择如何 总结到目前为止看到的输入会丢失必要的信息。

当您在语法文件中使用 '' 声明时, Bison 生成一个使用不同算法的解析器,称为 广义 LR(或 GLR)。野牛GLR 解析器使用相同的基本 解析为普通 Bison 解析器的算法,但行为 在不存在转移/减少冲突的情况下,情况有所不同 已通过优先级规则解析(请参阅运算符优先级)或 减少/减少冲突。当 GLR 解析器遇到这样的 情况,它 有效地拆分为多个解析器,每个解析器对应一个可能的解析器 移位或减少。然后,这些解析器像往常一样进行,消耗 令牌在锁步中。某些堆栈可能会遇到其他冲突 并进一步拆分,结果是不是一系列状态, Bison GLR 解析堆栈实际上是一棵状态树。%glr-parser

实际上,每个堆栈都表示对正确解析内容的猜测 是。额外的输入可能表明猜测是错误的,在这种情况下 相应的堆栈会静默消失。否则,语义 每个堆栈中生成的操作将被保存,而不是被执行 马上。当堆栈消失时,其保存的语义操作永远不会 被处决。当减少导致两个堆栈变得相等时, 它们的语义操作集都保存在以下状态中: 减少的结果。我们说两个堆栈是等价的 当它们都代表相同的状态序列时, 每对应的状态代表一个 生成输入标记的相同段的语法符号 流。

每当解析器从具有多个 状态为具有一个,它将恢复为正常的确定性解析 算法,在解析并执行保存的操作后。 在此转换中,堆栈上的某些状态将具有语义 值是可能操作的集合(实际上是多集)。这 Parser 尝试通过首先找到其规则的操作来选择其中一个操作 具有最高的动态优先级,由 '' 设置 声明。否则,如果替代操作不是由 优先级,但两者声明了相同的合并函数 规则由''声明, Bison 解析并计算两者,然后调用 结果。否则,它会报告歧义。%dprec%merge

可以对 GLR 解析树使用数据结构 允许在线性时间内处理任何 LR(1) 语法(在 输入的大小),任何明确(不一定 LR(1)) 语法 二次最坏情况时间,以及任何一般(可能模棱两可) 立方最坏情况下的上下文无关语法。然而,野牛目前 使用更简单的数据结构,该结构需要的时间与 输入长度乘以任何 输入的前缀。因此,确实模棱两可或不确定 语法可能需要指数级的时间和空间来处理。太糟糕了 然而,行为示例通常没有实际意义。 通常,语法中的非确定性是局部的——解析器是“在 怀疑“一次只针对几个令牌。因此,当前数据 结构一般应足够。在 LR(1) 部分 特别是语法,它只比 确定性 LR(1) Bison 解析器。

有关 GLR 解析器的更详细阐述,请参阅 Scott 2000


5.10 内存管理,以及如何避免内存耗尽

如果移动了太多令牌,则 Bison 解析器堆栈可能会耗尽内存,并且 不减少。发生这种情况时,解析器函数调用并返回 2。yyparseyyerror

因为 Bison 解析器的堆栈越来越大,达到了上限 通常是由于使用右递归而不是左递归 递归,请参阅递归规则

通过定义宏,您可以控制 解析器堆栈可以在内存耗尽之前成为。定义 值为整数的宏。此值是最大数字 在溢出之前可以转移(而不是减少)的令牌。YYMAXDEPTH

不一定分配允许的堆栈空间。如果指定 large 值 ,解析器通常分配一个小 首先堆叠,然后根据需要分阶段使其变大。这 增加分配是自动且无提示的。因此 你不需要仅仅为了节省而痛苦地渺小 不需要太多堆栈的普通输入的空间。YYMAXDEPTHYYMAXDEPTH

但是,不要允许值太大,以至于 计算堆栈大小时可能会发生算术溢出 空间。另外,不允许小于 .YYMAXDEPTHYYMAXDEPTHYYINITDEPTH

如果未定义 ,则 的默认值为 10000.YYMAXDEPTH

您可以通过定义 宏设置为正整数。对于确定性 解析器 在 C 中,此值必须是编译时常量 除非您假设使用 C99 或其他目标语言或编译器 这允许可变长度的数组。默认值为 200。YYINITDEPTH

不允许大于 。YYINITDEPTHYYMAXDEPTH

您可以生成包含C++用户代码的确定性解析器 默认 (C) 框架,以及来自 C++ 框架(请参阅 C++ 解析器)。但是,如果您确实使用默认骨架并希望允许 解析堆栈要增长,注意不要使用语义类型或位置 需要非平凡复制构造函数的类型。C 骨架旁路 这些构造函数在将数据复制到新的、更大的堆栈时。


下一篇: , 上一篇: , 上一篇: Bison [内容][索引]

6 错误恢复

让程序以语法终止通常是不可接受的 错误。例如,编译器应充分恢复以解析 输入文件的其余部分并检查其是否有错误;计算器应该接受 另一种表达方式。

在一个简单的交互式命令解析器中,每个输入都是一行,它可以 足以允许在错误时返回 1 并具有 发生这种情况时,调用方将忽略输入行的其余部分(然后再次调用)。但这对于编译器来说是不够的,因为它 忘记了导致错误的所有语法上下文。语法错误 在编译器输入的函数深处不应导致编译器 将以下行视为源文件的开头。yyparseyyparse

您可以通过将规则写入 识别特殊令牌。这是一个终端符号 始终定义(您不需要声明它)并保留错误 处理。Bison 解析器在 发生语法错误;如果已提供识别此令牌的规则 在当前上下文中,解析可以继续。errorerror

例如:

stmts:
  %empty
| stmts '\n'
| stmts exp '\n'
| stmts error '\n'

此示例中的第四条规则表示错误后跟换行符 对任何 .stmts

如果在 ?这 严格解释的错误恢复规则适用于精确序列 的 a、an 和 换行符。如果在 中间,可能会有一些额外的代币 和堆栈上的子表达式在最后一个 和 将是下一个换行符之前要读取的标记。所以规则不是 以普通方式适用。expstmtserrorexpstmts

但是野牛可以通过丢弃部分 语义上下文和部分输入。首先,它丢弃状态和 对象,直到它恢复到令牌可接受的状态。(这意味着子表达式 已经解析的被丢弃,回到最后完成。在 在这一点上,令牌可以移动。然后,如果旧的 解析器读取 Lookahead 令牌不可接受接下来移动 令牌并丢弃它们,直到找到可接受的令牌。在 在此示例中,Bison 读取和丢弃输入,直到下一个换行符,以便 第四条规则可以适用。请注意,丢弃的符号是可能的来源 的内存泄漏,请参阅释放丢弃的符号,了解回收此内容的方法 记忆。errorstmtserror

语法中错误规则的选择是策略的选择 错误恢复。一个简单而有用的策略是简单地跳过其余部分 如果检测到错误,则为当前输入行或当前语句:

stmt: error ';'  /* On error, skip until ';' is read. */

恢复到匹配的 close 分隔符也很有用 已解析的 open-delimiter。否则, close-delimiter 可能会看起来不匹配,并生成另一个, 虚假错误消息:

primary:
  '(' expr ')'
| '(' error ')'
…
;

错误恢复策略必然是猜测。当他们猜错时, 一个语法错误往往会导致另一个语法错误。在上面的示例中,错误 恢复规则猜测错误是由于 .假设在 中间的有效 .错误恢复规则从以下位置恢复后 第一个错误,另一个语法错误将立即被发现,因为 伪分号后面的文本也是无效的。stmtstmtstmt

为了防止错误消息的大量涌现,解析器不会输出任何错误 在第一个语法错误之后不久发生的另一个语法错误的消息;只 连续三个输入令牌成功转移后,将 错误消息恢复。

请注意,接受令牌的规则可能具有操作,只是 就像任何其他规则一样。error

通过在操作中使用宏,可以使错误消息立即恢复。如果在错误规则的操作中执行此操作,则不会 错误消息将被禁止显示。此宏不需要参数; '' 是有效的 C 语句。yyerrokyyerrok;

出现错误后,会立即重新分析以前的前瞻令牌。如果 这是不可接受的,那么宏可能会用于清除 这个令牌。在错误规则的 行动。 请参阅在操作中使用的特殊功能yyclearinyyclearin;

例如,假设在语法错误上,错误处理例程为 调用,将输入流推进到解析应该的某个点 再次开始。词法扫描程序返回的下一个符号是 可能是正确的。之前的 lookahead 令牌应该被丢弃 用 ''.yyclearin;

当解析器生成 1 时,表达式生成 1 正在从语法错误中恢复,否则为 0。 从语法中恢复时,语法错误诊断将被禁止显示 错误。YYRECOVERING ()


下一篇: , 上一篇: , 上一篇: Bison [Contents][Index]

7 处理上下文依赖关系

Bison 范式是先解析代币,然后将它们分组为更大的代币 句法单位。在许多语言中,令牌的含义受以下因素的影响 它的背景。尽管这违反了野牛范式,但某些技术 (称为 kludges)可能使您能够为此类编写 Bison 解析器 语言。

(实际上,“kludge”是指任何完成其工作但 既不干净也不坚固。


7.1 令牌种类中的语义信息

C 语言具有上下文依赖性:标识符的使用方式 取决于它现在的含义是什么。例如,请考虑以下情况:

foo (x);

这看起来像一个函数调用语句,但 if 是一个 typedef name,那么这其实就是一个声明。野牛怎么能 解析器 C 决定如何解析此输入?foox

GNU C 中使用的方法是具有两种不同的标记类型,以及 .当发现 identifier,它按顺序查找标识符的当前声明 确定要返回的令牌类型:如果标识符为 声明为 typedef,否则。IDENTIFIERTYPENAMEyylexTYPENAMEIDENTIFIER

然后,语法规则可以通过选择以下来表示上下文依赖性 要识别的令牌类型。 被接受为表达式, 但事实并非如此。 可以启动声明,但不能。在标识符含义的上下文中 并不重要,例如在可以掩盖 typedef 名称,要么就是 已接受 - 两种令牌类型中的每一种都有一个规则。IDENTIFIERTYPENAMETYPENAMEIDENTIFIERTYPENAMEIDENTIFIER

这种技术很容易使用,如果决定哪种 允许的标识符是在靠近标识符所在位置的位置创建的 解析。但在 C 中,情况并非总是如此:C 允许声明 如果指定了显式类型,请重新声明 typedef 名称 早些时候:

typedef int foo, bar;
int baz (void)
{
  static bar (bar);      /* redeclare bar as static variable */
  extern foo foo (foo);  /* redeclare foo as function */
  return foo (bar);
}

不幸的是,被声明的名称与声明是分开的 通过复杂的句法结构——“声明器”来构建自己。

因此,需要复制 C 的 Bison 解析器的一部分,使用 所有非终端名称都已更改:一次用于解析 可以重新定义 typedef 名称,并且一次用于解析 不能这样做的声明。这是 重复,为简洁起见省略了操作:

initdcl:
  declarator maybeasm '=' init
| declarator maybeasm
;
notype_initdcl:
  notype_declarator maybeasm '=' init
| notype_declarator maybeasm
;

这里可以重新声明 typedef 名称,但不能。和之间的区别是一回事。initdclnotype_initdcldeclaratornotype_declarator

这种技术与词汇搭配之间有一些相似之处 (如下所述),在改变词汇分析的信息中是 在程序的其他部分解析期间更改。区别在于 这里的信息是全局的,并用于其他目的 程序。真正的词汇搭配有一个特殊用途的标志,由 句法上下文。


7.2 词汇搭配

处理上下文依赖性的一种方法是词汇连接:标志 这是由 Bison 动作设置的,其目的是改变代币的方式 解析。

例如,假设我们有一种语言,模糊地像 C 语言,但有一个特殊的 构造 ''。关键字出现后 括号中所有整数均为十六进制的表达式。在 特别是,标记 '' 必须被视为整数,而不是 作为标识符(如果它出现在该上下文中)。这是你如何做到的:hex (hex-expr)hexa1b

%{
  int hexflag;
  int yylex (void);
  void yyerror (char const *);
%}
%%
…
expr:
  IDENTIFIER
| constant
| HEX '('        { hexflag = 1; }
    expr ')'     { hexflag = 0; $$ = $4; }
| expr '+' expr  { $$ = make_sum ($1, $3); }
…
;
constant:
  INTEGER
| STRING
;

在这里,我们假设 查看 的值 ;什么时候 它是非零的,所有整数都以十六进制解析,并且标记开始 如果可能,将字母解析为整数。yylexhexflag

语法文件序言中显示的声明 需要使操作可访问它(参见序言)。你必须 还要写代码以服从标志。hexflagyylex


7.3 词汇连接和错误恢复

词法绑定对您拥有的任何错误恢复规则都有严格的要求。 请参阅错误恢复

这样做的原因是错误恢复规则的目的是 中止一个构造的解析,并在某个更大的构造中恢复。 例如,在类 C 语言中,典型的错误恢复规则是跳过 标记,直到下一个分号,然后开始一个新的语句,如下所示:

stmt:
  expr ';'
| IF '(' expr ')' stmt { … }
…
| error ';'  { hexflag = 0; }
;

如果“”中间有语法错误 构造,则此错误规则将适用,然后操作 已完成的 '' 将永远不会运行。所以会 在输入的整个其余部分保持设置,或直到下一个关键字为止,导致标识符被误解为整数。hex (expr)hex (expr)hexflaghex

为避免此问题,错误恢复规则本身会清除 。hexflag

可能还存在在表达式中起作用的错误恢复规则。 例如,括号内可能有一条规则适用 并跳到右括号:

expr:
  …
| '(' expr ')'   { $$ = $2; }
| '(' error ')'
…

如果此规则在构造中起作用,则它不会中止 该结构(因为它适用于括号的内部级别 构造)。因此,它不应该清除标志:其余的 应使用仍然有效的标志来解析构造。hexhex

如果存在错误恢复规则,该规则可能会中止或可能不会中止,具体取决于具体情况,该怎么办?没有 编写操作以确定构造是否 是否中止。因此,如果您使用词汇搭配,则最好 请确保您的错误恢复规则不属于此类规则。每个规则必须 这样你就可以确定它总是会,或者总是不会,必须这样做 清除标志。hexhex


下一篇: , 上一篇: , 上一篇: Bison [内容][索引]

8 调试解析器

开发解析器可能是一个挑战,尤其是在您不了解的情况下 算法(参见 Bison 解析器算法)。本章解释如何理解 并调试解析器。

用户面临的最常见问题是解决他们的冲突。要修复它们, 第一步是了解它们在给定语法中是如何出现的。这是 通过自动生成反例变得更加容易,覆盖在 第一部分(参见反例的生成)。

不过,在大多数情况下,观察自动机的结构仍然是 需要。以下各节介绍如何生成和读取 自动机的详细结构描述。有几种格式 可用:

最后一部分重点介绍解析器的动态部分:如何启用 并了解解析器运行时跟踪(请参阅跟踪解析器)。


8.1 反例的生成

解决冲突可能是LR设计中最微妙的部分 解析器,如本文中专门介绍它们的章节数量所示 非常文档。要解决冲突,必须了解它:什么时候 它发生吗?是因为语法上的缺陷吗?是因为 LR(1) 无法应付这种语法?

一个困难是冲突发生在自动机中,它可以 将它们与语法本身的问题联系起来是很棘手的。跟 经验和耐心,分析详细描述的 automaton(参见 Understanding Your Parser)允许人们找到示例字符串 达到这些冲突。

由于反例的生成,这项任务变得更加容易, 最初由 Chinawat Isradisaikul 和 Andrew Myers 开发 (见 Isradisaikul 2015)。

作为第一个示例,请参阅 Shift/Reduce Conflicts 的语法,其特点是 一个班次/减少冲突:

$ bison else.y
else.y: warning: 1 shift/reduce conflict [-Wconflicts-sr]
else.y: note: rerun with option '-Wcounterexamples' to generate conflict counterexamples

让我们使用选项 / 重新运行:bison-Wcex-Wcounterexamples

else.y: warning: 1 shift/reduce conflict [-Wconflicts-sr]
else.y: warning: shift/reduce conflict on token "else" [-Wcounterexamples]
  Example: "if" expr "then" "if" expr "then" stmt  "else" stmt
  Shift derivation
    if_stmt
    ↳ 3: "if" expr "then" stmt
                           ↳ 2: if_stmt
                                 ↳ 4: "if" expr "then" stmt  "else" stmt
  Example: "if" expr "then" "if" expr "then" stmt  "else" stmt
  Reduce derivation
    if_stmt
    ↳ 4: "if" expr "then" stmt                                "else" stmt
                           ↳ 2: if_stmt
                                 ↳ 3: "if" expr "then" stmt 

这显示了一个表达式的两种不同推导,这证明了 语法模棱两可。


举一个更微妙的例子,考虑 Reduce/Reduce 冲突的示例语法,它具有 reduce/reduce 冲突:

%%
sequence:
  %empty
| maybeword
| sequence "word"
;
maybeword:
  %empty
| "word"
;

Bison 生成以下反例:

$ bison -Wcex sequence.y
sequence.y: warning: 1 shift/reduce conflict [-Wconflicts-sr]
sequence.y: warning: 2 reduce/reduce conflicts [-Wconflicts-rr]
sequence.y: warning: shift/reduce conflict on token "word" [-Wcounterexamples]
  Example:  "word"
  Shift derivation
    sequence
    ↳ 2: maybeword
          ↳ 5:  "word"
  Example:  "word"
  Reduce derivation
    sequence
    ↳ 3: sequence "word"
          ↳ 1: 
sequence.y: warning: reduce/reduce conflict on tokens $end, "word" [-Wcounterexamples]
  Example: 
  First reduce derivation
    sequence
    ↳ 1: 
  Example: 
  Second reduce derivation
    sequence
    ↳ 2: maybeword
          ↳ 4: 
sequence.y: warning: shift/reduce conflict on token "word" [-Wcounterexamples]
  Example:  "word"
  Shift derivation
    sequence
    ↳ 2: maybeword
          ↳ 5:  "word"
  Example:  "word"
  Reduce derivation
    sequence
    ↳ 3: sequence        "word"
          ↳ 2: maybeword
                ↳ 4: 
sequence.y:8.3-45: warning: rule useless in parser due to conflicts [-Wother]
    8 |   %empty    { printf ("empty maybeword\n"); }
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

这三个冲突中的每一个都再次证明了语法是模棱两可的。 例如,第二个冲突(reduce/reduce 冲突)表明 语法以两种不同的方式接受空输入。


有时,搜索不会找到可以分为两个的示例 方式。在这些情况下,反例生成将提供两个示例 直到点都是一样的。最值得注意的是,这将发生在以下情况下 你的语法需要一个更强的解析器(更多的前瞻性,LR 而不是 拉尔)。下面的例子不是 LR(1):

%token ID
%%
s: a ID
a: expr
expr: %empty | expr ID ','

bison报告:

ids.y: warning: 1 shift/reduce conflict [-Wconflicts-sr]
ids.y: warning: shift/reduce conflict on token ID [-Wcounterexamples]
  First example: expr  ID ',' ID $end
  Shift derivation
    $accept
    ↳ 0: s                                 $end
         ↳ 1: a                        ID
              ↳ 2: expr
                    ↳ 4: expr  ID ','
  Second example: expr  ID $end
  Reduce derivation
    $accept
    ↳ 0: s                   $end
         ↳ 1: a           ID
              ↳ 2: expr 
ids.y:4.4-7: warning: rule useless in parser due to conflicts [-Wother]
    4 | a: expr
      |    ^~~~

此冲突是由于分析器没有足够的信息来了解而引起的 这两个例子之间的区别。解析器需要一个 额外的 Lookahead 标记,用于了解逗号是否跟在 after .这些类型的冲突往往更多 很难修复,通常需要对语法进行返工。在这种情况下, 它可以通过围绕递归进行更改来修复:。IDexprexpr: ID | ',' expr ID

或者,您可能还需要考虑使用 GLR 解析器 (请参阅编写 GLR 解析器)。


有时,原位查看反例很有用:与 自动机报告(请参阅了解解析器,特别是状态 8)。


Next: , Previous: , Up: 调试解析器 [Contents][Index]

8.2 了解解析器

Bison 解析器是 shift/reduce 自动机(参见 Bison 解析器算法)。在一些 案例(比人们希望的要频繁得多),看看这个自动机是 需要调整或简单地修复解析器。

文本文件是在指定选项或时生成的,请参阅调用 Bison。它的名字是由 从解析器实现文件中删除“”或“” 名称,并改为添加“”。因此,如果语法文件是 ,则默认调用解析器实现文件。因此,详细的输出文件称为 。--report--verbose.tab.c.c.outputfoo.yfoo.tab.cfoo.output

以下语法文件 ,将在续集中使用:calc.y

%union
{
  int ival;
  const char *sval;
}
%token <ival> NUM
%nterm <ival> exp
%token <sval> STR
%nterm <sval> useless
%left '+' '-'
%left '*'
%%
exp:
  exp '+' exp
| exp '-' exp
| exp '*' exp
| exp '/' exp
| NUM
;
useless: STR;
%%

bison报告:

calc.y: warning: 1 nonterminal useless in grammar [-Wother]
calc.y: warning: 1 rule useless in grammar [-Wother]
calc.y:19.1-7: warning: nonterminal useless in grammar: useless [-Wother]
   19 | useless: STR;
      | ^~~~~~~
calc.y: warning: 7 shift/reduce conflicts [-Wconflicts-sr]
calc.y: note: rerun with option '-Wcounterexamples' to generate conflict counterexamples

回到 calc 示例,当给定时, 除了 之外,它还创建一个文件,其内容详述如下。输出的顺序和精确的顺序 表述可能会有所不同,但解释是相同的。--report=statecalc.tab.ccalc.output

第一部分报告无用的令牌、非终端和规则。无用 为了生成更小的解析器,删除了非终端和规则,但 无用的令牌会被保留,因为它们可能会被扫描程序使用(注意 下面“无用”和“未使用”之间的区别):

Nonterminals useless in grammar
   useless

Terminals unused in grammar
   STR

Rules useless in grammar
    6 useless: STR

下一节将列出仍然存在冲突的状态。

State 8 conflicts: 1 shift/reduce
State 9 conflicts: 1 shift/reduce
State 10 conflicts: 1 shift/reduce
State 11 conflicts: 4 shift/reduce

然后 Bison 重现了它使用的确切语法:

Grammar

    0 $accept: exp $end

    1 exp: exp '+' exp
    2    | exp '-' exp
    3    | exp '*' exp
    4    | exp '/' exp
    5    | NUM

并报告符号的用法:

Terminals, with rules where they appear

    $end (0) 0
    '*' (42) 3
    '+' (43) 1
    '-' (45) 2
    '/' (47) 4
    error (256)
    NUM <ival> (258) 5
    STR <sval> (259)
Nonterminals, with rules where they appear

    $accept (9)
        on left: 0
    exp <ival> (10)
        on left: 1 2 3 4 5
        on right: 0 1 2 3 4

然后,野牛继续进入自动机本身,用 它的集,也称为虚线规则。每个项目都是一个 生产规则以及标记位置的点 ('') 输入光标。.

State 0

    0 $accept: • exp $end

    NUM  shift, and go to state 1

    exp  go to state 2

其内容如下:“状态 0 对应于处于非常 解析的开始,在初始规则中,就在开始之前 符号(这里,)。当解析器返回到此状态时,权限 在简化生成 的规则后,控件 流跳转到状态 2。如果非终端上没有这样的转换 符号,并且前瞻是 ,则此标记被转移到 解析堆栈和控制流跳转到状态 1。任何其他 Lookahead 会触发语法错误。expexpNUM

尽管状态 0 中唯一的活动规则似乎是规则 0,但 报告列为展望令牌,因为可以 在任何派生 . 的规则的开头默认情况下,Bison 报告项集的所谓核心内核,但如果 您还希望查看更多详细信息,以便调用以列出派生项:NUMNUMexpbison--report=itemset

State 0

    0 $accept: • exp $end
    1 exp: • exp '+' exp
    2    | • exp '-' exp
    3    | • exp '*' exp
    4    | • exp '/' exp
    5    | • NUM

    NUM  shift, and go to state 1

    exp  go to state 2

在该州 1...

State 1

    5 exp: NUM •

    $default  reduce using rule 5 (exp)

规则 5 “”已完成。无论前瞻令牌是什么 (''),解析器将减少它。如果它来自国家 0,那么,在此减少之后,它将返回到状态 0,并将跳转到 状态 2 ('')。exp: NUM;$defaultexp: go to state 2

State 2

    0 $accept: exp • $end
    1 exp: exp • '+' exp
    2    | exp • '-' exp
    3    | exp • '*' exp
    4    | exp • '/' exp

    $end  shift, and go to state 3
    '+'   shift, and go to state 4
    '-'   shift, and go to state 5
    '*'   shift, and go to state 6
    '/'   shift, and go to state 7

在状态 2 中,自动机只能移动一个符号。例如,因为 项目 '',如果前瞻是 '',则为 移到解析堆栈上,自动机跳转到状态 4, 对应于项目 ''。由于没有 默认操作,任何未列出的展望都会触发语法错误。exp: exp • '+' exp+exp: exp '+' • exp

状态 3 被命名为最终状态,或接受状态 状态

State 3

    0 $accept: exp $end •

    $default  accept

初始规则完成(开始符号和输入结束符为 read),解析成功退出。

状态 4 到 7 的解释很简单,留给 读者。

State 4

    1 exp: exp '+' • exp

    NUM  shift, and go to state 1

    exp  go to state 8


State 5

    2 exp: exp '-' • exp

    NUM  shift, and go to state 1

    exp  go to state 9


State 6

    3 exp: exp '*' • exp

    NUM  shift, and go to state 1

    exp  go to state 10


State 7

    4 exp: exp '/' • exp

    NUM  shift, and go to state 1

    exp  go to state 11

正如报告开头所宣布的那样,“:State 8 conflicts: 1 shift/reduce

State 8

    1 exp: exp • '+' exp
    1    | exp '+' exp •
    2    | exp • '-' exp
    3    | exp • '*' exp
    4    | exp • '/' exp

    '*'  shift, and go to state 6
    '/'  shift, and go to state 7

    '/'       [reduce using rule 1 (exp)]
    $default  reduce using rule 1 (exp)

事实上,有两个操作与前瞻 '' 相关联: 要么移动(并转到状态 7),要么减少规则 1。这 冲突意味着语法不明确,或者解析器缺乏 做出正确决定的信息。事实上,语法是 模棱两可,因为,由于我们没有指定 '' 的优先级,因此 句子 '' 可以解析为 '',对应于移动 '',也可以解析为 '',对应于减少规则 1。//NUM + NUM / NUMNUM + (NUM / NUM)/(NUM + NUM) / NUM

因为在确定性解析中,可以做出一个单一的决定,Bison 任意选择禁用缩减,请参阅 Shift/Reduce 冲突。 放弃的操作在方括号之间报告。

请注意,之前的所有状态都有一个可能的操作:要么 移动下一个令牌并转到相应的状态,或者 减少单个规则。在其他情况下,即当可以移位减少时,或者当多次减少时 可能,需要提前查看才能选择操作。状态 8 是 一种这样的状态:如果前瞻是“”或“”,则操作 正在移动,否则操作将减少规则 1。换言之, 与规则 1 相对应的前两项不符合条件,如果 Lookahead token 是 '',因为我们指定 '' 具有更高的 优先于“”。更一般地说,某些项目仅符合条件 具有一些可能的 Lookahead 令牌。当使用 时,Bison 指定以下 lookahead 令牌:*/**+--report=lookahead

State 8

    1 exp: exp • '+' exp
    1    | exp '+' exp •  [$end, '+', '-', '/']
    2    | exp • '-' exp
    3    | exp • '*' exp
    4    | exp • '/' exp

    '*'  shift, and go to state 6
    '/'  shift, and go to state 7

    '/'       [reduce using rule 1 (exp)]
    $default  reduce using rule 1 (exp)

但请注意,虽然 '' 是不明确的(这会导致 '')、'' 上的冲突不是:冲突是 由于关联性和优先指令而得到解决。如果使用 调用,则 Bison 包含有关已求解的信息 报告中的冲突:NUM + NUM / NUM/NUM + NUM * NUM--report=solved

Conflict between rule 1 and token '+' resolved as reduce (%left '+').
Conflict between rule 1 and token '-' resolved as reduce (%left '-').
Conflict between rule 1 and token '*' resolved as shift ('+' < '*').

当给定时,将生成 报告中的反例,并增加了相应的项目 (参见反例的生成)。--report=counterexamplesbison

shift/reduce conflict on token '/':
    1 exp: exp '+' exp •
    4 exp: exp • '/' exp
  Example: exp '+' exp • '/' exp
  Shift derivation
    exp
    ↳ 1: exp '+' exp
                 ↳ 4: exp • '/' exp
  Example: exp '+' exp • '/' exp
  Reduce derivation
    exp
    ↳ 4: exp                 '/' exp
         ↳ 1: exp '+' exp •

这显示了同一语法中的两个独立派生: ‘’.派生显示了您的规则将如何解析 举个例子。在这里,第一个推导在看到时完成约简 '',导致 '' 被分组为 .第二个 导数在 '' 上移动,导致 '' 被分组为 一。因此,很容易看出,添加 优先级/关联性指令将解决此冲突。expe1 + e2 / e3/e1 + e2exp/e2 / e3exp

其余状态类似:

State 9

    1 exp: exp • '+' exp
    2    | exp • '-' exp
    2    | exp '-' exp •
    3    | exp • '*' exp
    4    | exp • '/' exp

    '*'  shift, and go to state 6
    '/'  shift, and go to state 7

    '/'       [reduce using rule 2 (exp)]
    $default  reduce using rule 2 (exp)
State 10

    1 exp: exp • '+' exp
    2    | exp • '-' exp
    3    | exp • '*' exp
    3    | exp '*' exp •
    4    | exp • '/' exp

    '/'  shift, and go to state 7

    '/'       [reduce using rule 3 (exp)]
    $default  reduce using rule 3 (exp)
State 11

    1 exp: exp • '+' exp
    2    | exp • '-' exp
    3    | exp • '*' exp
    4    | exp • '/' exp
    4    | exp '/' exp •

    '+'  shift, and go to state 4
    '-'  shift, and go to state 5
    '*'  shift, and go to state 6
    '/'  shift, and go to state 7

    '+'       [reduce using rule 4 (exp)]
    '-'       [reduce using rule 4 (exp)]
    '*'       [reduce using rule 4 (exp)]
    '/'       [reduce using rule 4 (exp)]
    $default  reduce using rule 4 (exp)

请注意,状态 11 包含冲突,这不仅是因为缺少 “”相对于“”、“”和“”的优先级,但 也因为未指定“”的关联性。/+-*/

Bison 还可以通过 XML 文件生成此输出的 HTML 版本,并且 XSLT 处理(请参阅以多种格式可视化分析器)。


下一篇: , 上一篇: 器, 上一篇: 调试解析器 [内容][索引]

8.3 可视化解析器

作为更好地了解移位/减少的另一种方法 自动机对应 Bison 解析器,可以生成一个 DOT 文件。注意 用这个调试真正的语法充其量是乏味的,而且不切实际 大多数时候,因为生成的文件很大(生成 其中的 PDF 或 PNG 文件将花费很长时间,而且通常情况下会 由于内存耗尽而失败)。此选项是为初学者设计的, 以帮助他们理解 LR 解析器。

指定选项时生成此文件 (请参阅调用 Bison)。它的名字是通过删除 '' 或 '' 从解析器实现文件名中获取,以及 改为添加“”。如果语法文件是 , Graphviz 输出文件称为 。DOT文件也可以是 通过 XML 文件和 XSLT 处理生成(请参阅以多种格式可视化分析器)。--graph.tab.c.c.gvfoo.yfoo.gv

以下语法文件 ,将在续集中使用:rr.y

%%
exp: a ";" | b ".";
a: "0";
b: "0";

图形输出 (见图 8.1) 与文本非常相似,因此更容易理解 对它们进行直接比较。请参阅调试分析器,了解详细信息 对文本报告的分析。

无花果/示例

图 8.1: 分析器的图形呈现。

状态的图形表示

每个状态的项目(虚线规则)在图形节点中组合在一起。 它们的编号与详细文件中的编号相同。请参阅以下内容 点,关于过渡,例如

当使用 调用时,前瞻令牌,当 需要,在方括号之间的相关规则旁边显示为 逗号分隔列表。图中表示的情况就是这种情况 减少,如下。--report=lookaheads


转换表示为电流和 目标状态。

班次的图形表示

移位显示为实心箭头,标有 lookahead 标记 转变。下面介绍了文件中的缩减:rr.output

State 3

    1 exp: a • ";"

    ";"  shift, and go to state 6

图形的这一部分的 Graphviz 渲染可以是:

无花果/示例移位

减少的图形表示

减少显示为实心箭头,指向菱形节点 承载减少规则的编号。箭头标有 适当的逗号分隔的 Lookahead 令牌。如果减少是默认值 对于给定状态的操作,没有这样的标签。

这是在详细文件中表示减少的方式:rr.output

State 1

    3 a: "0" •  [";"]
    4 b: "0" •  ["."]

    "."       reduce using rule 4 (b)
    $default  reduce using rule 3 (a)

图形的这一部分的 Graphviz 渲染可以是:

无花果/示例-reduce

当存在未解决的冲突时,因为在确定性解析中 可以做出一个决定,Bison可以任意选择禁用一个 reduction,请参阅 Shift/Reduce 冲突。放弃的操作 在这些节点上以红色填充颜色来区分,就像它们一样 在详细文件中的方括号之间报告。

与规则编号 0 相对应的减少是接受 州。它显示为一颗蓝色钻石,标有“Acc”。

Gotos的图形表示

'' 跳跃过渡表示为虚线方位 要跳转到的规则的名称。go to


Next: , Previous: , Up: 调试解析器 [内容][索引]

8.4 以多种格式可视化解析器

Bison 支持两种主要的报告格式:文本输出 (请参阅了解解析器) 调用时 带选项和 DOT (请参阅可视化解析器) 当调用时 选择。然而 另一种替代方法是输出一个 XML 文件,然后可以将 呈现为相当于 详细文件,或作为同一文件的 HTML 版本,可单击 过渡,甚至作为 DOT。和 DOT 文件通过以下方式获取 XSLT 与通过调用 options 或 .--verbose--graphxsltproc.outputbison--verbose--graph

XML 文件是在指定选项或时生成的,请参阅调用 Bison。 如果未指定,则通过删除“”或“”来命名 从解析器实现文件名中,然后添加“”。 例如,如果语法文件是 ,则默认 XML 输出 文件是 。-x--xml[=FILE].tab.c.c.xmlfoo.yfoo.xml

Bison 附带一个目录,其中包含 XSL Transformation 要应用于 XML 文件的文件。它们的名称是明确的:data/xslt

xml2dot.xsl

用于输出自动机的 DOT 可视化的副本。

xml2text.xsl

用于输出“”文件的副本。.output

xml2xhtml.xsl

用于输出 '' 文件的 xhtml 增强功能。.output

示例用法(需要):xsltproc

$ bison -x gr.y
$ bison --print-datadir
/usr/local/share/bison
$ xsltproc /usr/local/share/bison/xslt/xml2xhtml.xsl gr.xml >gr.html

上一篇: 以, 上一篇: 调试解析器 [内容][索引]

8.5 跟踪解析器

当 Bison 语法编译正确但解析“不正确”时,解析器跟踪功能有助于找出原因。yydebug


8.5.1 启用跟踪

有几种方法可以编译跟踪设施,在 优先顺序递减:

变量 'parse.trace'

添加 '' 指令(请参阅 %define Summary),或传递选项 (请参阅调整解析器)。这是 Bison 扩展。除非 POSIX 和 Yacc 的便携性对您很重要,这是首选解决方案。%define parse.trace-Dparse.trace

选项 -t(符合 POSIX Yacc)
选项 --debug (Bison 扩展名)

运行 Bison 时使用该选项(请参阅调用 Bison)。跟 '',它定义为 1,否则它 定义为 1。-t%define api.prefix {c}CDEBUGYYDEBUG

指令 '%debug' (已弃用)

添加指令(请参阅 Bison 声明摘要)。这头野牛 维护扩展以实现向后兼容性;请改用。%debug%define parse.trace

YYDEBUG(仅限 C/C++)

将宏定义为非零值时编译 解析 器。这符合 POSIX Yacc 标准。您可以用作编译器选项,也可以将 '' 放在语法文件的序言中(参见序言)。YYDEBUG-DYYDEBUG=1#define YYDEBUG 1

如果使用变量(请参阅同一程序中的多个解析器),例如 '',则如果定义了变量,则其值控制 跟踪功能(当且仅当非零时启用);否则跟踪是 当且仅当为非零时启用。%defineapi.prefix%define api.prefix {c}CDEBUGYYDEBUG

在 C++ 中,POSIX 合规性没有意义,请避免此选项,而首选 ‘’.如果你的宏 在错误的地方(例如,在 '' 而不是 ''),解析器类将有两个不同的定义,因此 导致 ODR 违规和愉快的调试时间。%define parse.trace#defineYYDEBUG%code top%code require

我们建议您始终启用 trace 选项,以便调试 总是可能的。

在 C 语言中,跟踪工具输出带有宏调用的消息,其形式为 where 和 是常用格式和可变参数。如果 定义为非零值但不定义 ,将自动包含并定义为 。YYFPRINTF (stderr, format, args)formatargsprintfYYDEBUGYYFPRINTF<stdio.h>YYFPRINTFfprintf

一旦你编译了带有跟踪工具的程序,请求的方式 跟踪是在变量中存储非零值。您可以 通过让 C 代码来做到这一点(也许),或者你可以 使用 C 调试器更改值。yydebugmain

解析器在非零时执行的每个步骤都会生成一行 或两个跟踪信息,写在.跟踪消息 告诉你这些事情:yydebugstderr

  • 每次解析器调用时,读取了什么样的令牌。yylex
  • 每次移动令牌时,状态的深度和完整内容 stack(请参阅解析器状态)。
  • 每次减少规则时,它是哪条规则,以及 之后的状态堆栈。

为了理解这些信息,参考自动机会有所帮助 description 文件(请参阅了解解析器)。这 文件显示了每个状态在各种规则中的位置的含义, 以及每个状态将如何处理每个可能的输入令牌。作为你 阅读连续的跟踪消息,可以看到解析器是 根据其在列表文件中的规范运行。最终 你会到达发生不良事情的地方,而你 将看到语法的哪些部分是罪魁祸首。

解析器实现文件是一个 C/C++/D/Java 程序,您可以使用 调试器,但要解释它在做什么并不容易。这 Parser 函数是一个有限状态机解释器,除了 操作:它一遍又一遍地执行相同的代码。只有 变量显示它在语法中的位置。


Previous: , 向上: 跟踪解析器 [Contents][索引]

8.5.2 启用调试跟踪mfcalc

调试信息通常给出读取的每个令牌的令牌类型, 但不是它的语义值。该指令允许指定 如何报告语义值,请参阅打印语义值%printer

作为演示,考虑多功能 计算器,(参见多功能计算器:mfcalc)。启用运行时 跟踪和语义值报告,在其 序幕:%printermfcalc

/* Generate the parser description file. */
%verbose
/* Enable run-time traces (yydebug). */
%define parse.trace

/* Formatting semantic values. */
%printer { fprintf (yyo, "%s", $$->name); } VAR;
%printer { fprintf (yyo, "%s()", $$->name); } FUN;
%printer { fprintf (yyo, "%g", $$); } <double>;

该指令指示 Bison 生成运行时跟踪 支持。然后,这些跟踪的激活在运行时由变量控制,默认情况下该变量处于禁用状态。因为这些痕迹 将参考解析器的“状态”,询问 创建该解析器的描述;这就是(诚然 Ill-named) 指令。%defineyydebug%verbose

这组指令演示了如何格式化 跟踪中的语义值。请注意,可以完成规范 在符号类型(例如,或)或类型上 标签: 因为 是 和 的类型 , 这台打印机将用于他们。%printerVARFUN<double>NUMexp

下面是运行时跟踪提供的信息示例。痕迹 被发送到标准错误。

$ echo 'sin(1-1)' | ./mfcalc -p
Starting parse
Entering state 0
Reducing stack by rule 1 (line 34):
-> $$ = nterm input ()
Stack now 0
Entering state 1

第一批显示了此语法的特定功能:第一条规则 (在第 34 行中,甚至可以在没有 查找第一个令牌。得到的左侧符号 () 是 无值 ('') 非终端 ()。mfcalc.y$$()inputnterm

然后,解析器调用扫描程序。

Reading a token
Next token is token FUN (sin())
Shifting token FUN (sin())
Entering state 6

该标记 () 是一个函数 (),其值为 '',按照我们的规范格式化:''。 解析器存储 () 该令牌和其他令牌,直到它可以执行 关于它的东西。tokenFUNsin%printersin()Shifting

Reading a token
Next token is token '(' ()
Shifting token '(' ()
Entering state 14
Reading a token
Next token is token NUM (1.000000)
Shifting token NUM (1.000000)
Entering state 4
Reducing stack by rule 6 (line 44):
   $1 = token NUM (1.000000)
-> $$ = nterm exp (1.000000)
Stack now 0 1 6 14
Entering state 24

前面的简化演示了以下指令:令牌和生成的非终端都具有 '' 作为值。%printer<double>NUMexp1

Reading a token
Next token is token '-' ()
Shifting token '-' ()
Entering state 17
Reading a token
Next token is token NUM (1.000000)
Shifting token NUM (1.000000)
Entering state 4
Reducing stack by rule 6 (line 44):
   $1 = token NUM (1.000000)
-> $$ = nterm exp (1.000000)
Stack now 0 1 6 14 24 17
Entering state 26
Reading a token
Next token is token ')' ()
Reducing stack by rule 11 (line 49):
   $1 = nterm exp (1.000000)
   $2 = token '-' ()
   $3 = nterm exp (1.000000)
-> $$ = nterm exp (0.000000)
Stack now 0 1 6 14
Entering state 24

减法的规则只是减少了。解析器即将 发现对 的调用结束。sin

Next token is token ')' ()
Shifting token ')' ()
Entering state 31
Reducing stack by rule 9 (line 47):
   $1 = token FUN (sin())
   $2 = token '(' ()
   $3 = nterm exp (0.000000)
   $4 = token ')' ()
-> $$ = nterm exp (0.000000)
Stack now 0 1
Entering state 11

最后,行尾允许解析器完成计算,并且 显示其结果。

Reading a token
Next token is token '\n' ()
Shifting token '\n' ()
Entering state 22
Reducing stack by rule 4 (line 40):
   $1 = nterm exp (0.000000)
   $2 = token '\n' ()
⇒ 0
-> $$ = nterm line ()
Stack now 0 1
Entering state 10
Reducing stack by rule 2 (line 35):
   $1 = nterm input ()
   $2 = nterm line ()
-> $$ = nterm input ()
Stack now 0
Entering state 1

解析器已返回到状态 1,在该状态中,它正在等待下一个 表达式的计算方法,或者用于文件末尾令牌,这会导致 完成解析。

Reading a token
Now at end of input.
Shifting token $end ()
Entering state 2
Stack now 0 1 2
Cleanup: popping token $end ()
Cleanup: popping nterm input ()

下一篇: , 上一篇: , 上一篇: Bison [Contents][Index]

9 调用野牛

调用 Bison 的常用方法如下:

$ bison file

这是语法文件名,通常以“”结尾。 解析器实现文件的名称是通过替换“' 替换为 '' 并删除任何前导目录。因此, '' 文件名生成 ,'' 文件名生成 。以防万一 您正在语法文件中编写 C++ 代码而不是 C,以命名它或 .然后,输出文件将采用 扩展,如给定的扩展作为输入(分别为 和 )。此功能对以下所有选项都有效 操作文件名,如 或 .file.y.y.tab.cbison foo.yfoo.tab.cbison hack/foo.yfoo.tab.cfoo.yppfoo.y++foo.tab.cppfoo.tab.c++-o-d

例如:

$ bison -d file.yxx

将产生 和 、 和file.tab.cxxfile.tab.hxx

$ bison -d -o output.c++ file.y

将产生 和 .output.c++output.h++

为了与 POSIX 兼容,标准 Bison 发行版还包含 一个名为 shell 脚本,该脚本使用选项调用 Bison。yacc-y


退出状态为:bison

0 (成功)

当没有错误时。警告,这是对可疑的诊断 构造,不要更改退出状态,除非它们被变成 错误(请参阅 -Werror)。

1 (失败)

当出现错误时。未生成任何文件(生成的报告除外) by 等)。特别是,输出文件可能 存在没有改变。--verbose

63(不匹配)

当不满足语法的版本要求时 文件。请参阅需要 Bison 版本。未生成或更改任何文件。bison


下一篇: ,向上:调用 Bison [内容][索引]

9.1 野牛选项

Bison 支持传统的单字母选项和助记符长 选项名称。长选项名称用 而不是 表示。允许使用期权名称的缩写,只要它们 是独一无二的。当长选项采用参数时,例如 ,将选项名称和参数连接起来 ‘’.-----file-prefix=

以下是可用于 Bison 的选项列表。它后面跟着一个 按长选项字母顺序排列的十字键。


下一篇: , 上一篇: 野牛选项 [内容][索引]

9.1.1 操作模式

控制 的全局行为的选项。bison

-h
--help

将命令行选项的摘要打印到 Bison 并退出。

-V
--version

打印 Bison 的版本号并退出。

--print-localedir

打印包含与区域设置相关的数据的目录的名称。

--print-datadir

打印包含框架、CSS 和 XSLT 的目录的名称。

-u
--update

更新语法文件(删除重复项、更新已弃用的指令、 等)并退出(即不生成任何输出文件)。留下一个 备份原始文件,并附加一个。例如:~

$ cat foo.y
%error-verbose
%define parse.error verbose
%%
exp:;
$ bison -u foo.y
foo.y:1.1-14: warning: deprecated directive, use '%define parse.error verbose' [-Wdeprecated]
    1 | %error-verbose
      | ^~~~~~~~~~~~~~
foo.y:2.1-27: warning: %define variable 'parse.error' redefined [-Wother]
    2 | %define parse.error verbose
      | ^~~~~~~~~~~~~~~~~~~~~~~~~~~
foo.y:1.1-14:     previous definition
    1 | %error-verbose
      | ^~~~~~~~~~~~~~
bison: file 'foo.y' was updated (backup: 'foo.y~')
$ cat foo.y
%define parse.error verbose
%%
exp:;

有关详细信息,请参阅下面的文档。--feature=fixit

-f [feature]
--feature[=feature]

激活杂项。 可以是以下之一:featureFeature

caret
diagnostics-show-caret

以类似于 GCC 或 Clang 的方式显示插入符号错误。使用消息提供的位置 引用源文件的相应行,在 它的重要部分与插入符号 ('')。下面是一个示例,使用 以下文件:-fdiagnostics-show-caret-fcaret-diagnostics^in.y

%nterm <ival> exp
%%
exp: exp '+' exp { $exp = $1 + $2; };

当调用(或不调用)时,Bison 将报告:-fcaret

in.y:3.20-23: error: ambiguous reference: '$exp'
    3 | exp: exp '+' exp { $exp = $1 + $2; };
      |                    ^~~~
in.y:3.1-3:       refers to: $exp at $$
    3 | exp: exp '+' exp { $exp = $1 + $2; };
      | ^~~
in.y:3.6-8:       refers to: $exp at $1
    3 | exp: exp '+' exp { $exp = $1 + $2; };
      |      ^~~
in.y:3.14-16:     refers to: $exp at $3
    3 | exp: exp '+' exp { $exp = $1 + $2; };
      |              ^~~
in.y:3.32-33: error: $2 of 'exp' has no declared type
    3 | exp: exp '+' exp { $exp = $1 + $2; };
      |                                ^~

然而,当使用 调用时,Bison 只会报告:-fno-caret

in.y:3.20-23: error: ambiguous reference: '$exp'
in.y:3.1-3:       refers to: $exp at $$
in.y:3.6-8:       refers to: $exp at $1
in.y:3.14-16:     refers to: $exp at $3
in.y:3.32-33: error: $2 of 'exp' has no declared type

默认情况下,此选项处于激活状态。

fixit
diagnostics-parseable-fixits

以类似于 GCC 和 Clang 的方式显示机器可读的修复程序。-fdiagnostics-parseable-fixits

为重复指令生成修复程序:

$ cat foo.y
%define api.prefix {foo}
%define api.prefix {bar}
%%
exp:;
$ bison -ffixit foo.y
foo.y:2.1-24: error: %define variable 'api.prefix' redefined
    2 | %define api.prefix {bar}
      | ^~~~~~~~~~~~~~~~~~~~~~~~
foo.y:1.1-24:     previous definition
    1 | %define api.prefix {foo}
      | ^~~~~~~~~~~~~~~~~~~~~~~~
fix-it:"foo.y":{2:1-2:25}:""
foo.y: warning: fix-its can be applied.  Rerun with option '--update'. [-Wother]

它们也是为了更新已弃用的指令而生成的,除非给出:-Wno-deprecated

$ cat /tmp/foo.yy
%error-verbose
%name-prefix "foo"
%%
exp:;
$ bison foo.y
foo.y:1.1-14: warning: deprecated directive, use '%define parse.error verbose' [-Wdeprecated]
    1 | %error-verbose
      | ^~~~~~~~~~~~~~
foo.y:2.1-18: warning: deprecated directive, use '%define api.prefix {foo}' [-Wdeprecated]
    2 | %name-prefix "foo"
      | ^~~~~~~~~~~~~~~~~~
foo.y: warning: fix-its can be applied.  Rerun with option '--update'. [-Wother]

当给定选项 / 时,fix-its 将自行应用。请参阅上面的文档。bison-u--update

syntax-only

不要生成输出文件。这个功能的名称有点 误导,因为不仅仅是检查语法:每个阶段都在运行 (包括检查冲突),除了生成 输出文件。


下一篇: , 上一篇: , 上一篇: 野牛选项 [内容][索引]

9.1.2 诊断

控制诊断的选项。

-W [category]
--warnings[=category]

输出警告落在 . 可以是一个 之:categorycategory

conflicts-sr
conflicts-rr

S/R 和 R/R 冲突。默认情况下,这些警告处于启用状态。但是,如果 指定了 OR 指令,即 意外的冲突数是一个错误,预期的冲突数 冲突没有报告,所以然后有 对冲突报告没有影响。%expect%expect-rr-W--warning

counterexamples
cex

提供冲突的反例。请参阅反例的生成。 反例需要时间来计算。该选项应为 开发人员在处理语法时使用;这几乎没有意义 在 CI 中使用它。-Wcex

dangling-alias

报告未绑定到令牌符号的字符串文本。

字符串文字,允许更好的错误消息,(太)自由 被 Bison 接受,这可能会导致静默错误。例如

%type <exVal> cond "condition"

未将“condition”定义为 —nonterminal 的字符串别名 符号没有字符串别名。它相当等价于cond

%nterm <exVal> cond
%token <exVal> "condition"

即,它为''令牌提供类型。"condition"exVal

此外,由于不需要定义字符串别名,因此 “”而不是“”将不会被报告。"baz""bar"

该选项可捕获这些情况。上-Wdangling-alias

%token BAR "bar"
%type <ival> foo "foo"
%%
foo: "baz" {}

'' 报告bison -Wdangling-alias

warning: string literal not attached to a symbol
      | %type <ival> foo "foo"
      |                  ^~~~~
warning: string literal not attached to a symbol
      | foo: "baz" {}
      |      ^~~~~
deprecated

已弃用的构造,其支持将在 将来的 野牛。

empty-rule

没有 .请参阅空规则。禁用者 默认值,但通过使用 启用,除非指定。%empty%empty-Wno-empty-rule

midrule-values

警告已设置但未在任何操作中使用的中间规则值 父规则。 例如,警告未在以下情况下使用:$2

exp: '1' { $$ = 1; } '+' exp { $$ = $1 + $4; };

还要警告已使用但未设置的中间规则值。 例如,警告以下 midrule 操作中的未设置:$$

exp: '1' { $1 = 1; } '+' exp { $$ = $2 + $4; };

默认情况下不启用这些警告,因为它们有时会证明 是使用 Yacc 结构的现有语法中的误报,或者(其中是一些正整数)。$0$-nn

precedence

无用的优先级和关联性指令。默认情况下处于禁用状态。

例如,考虑以下语法:

%nonassoc "="
%left "+"
%left "*"
%precedence "("
%%
stmt:
  exp
| "var" "=" exp
;
exp:
  exp "+" exp
| exp "*" "number"
| "(" exp ")"
| "number"
;

野牛报告:

warning: useless precedence and associativity for "="
      | %nonassoc "="
      |           ^~~
warning: useless associativity for "*", use %precedence
      | %left "*"
      |       ^~~
warning: useless precedence for "("
      | %precedence "("
      |             ^~~

人们将获得与以下指令完全相同的解析器:

%left "+"
%precedence "*"
yacc

与 POSIX Yacc 不兼容。

other

上面未分类的所有警告。默认情况下,这些警告处于启用状态。

提供此类别只是为了完整起见。前途 Bison 的版本可能会将警告从此类别移动到新的、更具体的警告 类别。

all

除 和 之外的所有警告。counterexamplesdangling-aliasyacc

none

关闭所有警告。

error

请参阅下文。-Werror

可以通过在类别名称前加上“”来关闭类别。为 实例,将隐藏有关 POSIX Yacc 不兼容。no--Wno-yacc

-Werror

将每个启用的警告转换为错误,除非它们是 显式禁用。category-Wno-error=category

-Werror=category

启用 的警告,并将它们视为错误。category

category与 相同,不同之处在于 它不能以“”为前缀(见上文)。--warningsno-

请注意,'' 和 '' 运算符的优先级是这样的 以下命令等效,因为第一个命令不会处理 S/R 冲突为错误。=,

$ bison -Werror=yacc,conflicts-sr input.y
$ bison -Werror=yacc,error=conflicts-sr input.y
-Wno-error

不要将启用的警告转换为错误,除非 它们由 显式启用。category-Werror=category

-Wno-error=category

停用此 .但是,警告 此选项不会禁用或启用本身。category

--color

等效于 。--color=always

--color=when

控制诊断是否着色,具体取决于:when

always
yes

启用彩色诊断。

never
no

禁用彩色诊断。

auto (default)
tty

如果输出设备是 tty,则诊断将被着色,即当 输出直接转到文本屏幕或终端仿真器窗口。

--style=file

指定着色时要使用的 CSS 样式。它有效果 仅当选项有效时。该文件提供了一个很好的示例,从中定义 您自己的样式文件。有关更多信息,请参阅 libtextstyle 的文档 详。file--colorbison-default.css


Next: , Previous: , Up: Bison Options [Contents][Index]

9.1.3 调整解析器

更改生成的解析器的选项。

-t
--debug

在分析器实现文件中,如果 它尚未定义,因此编译调试工具。 请参阅跟踪分析器YYDEBUG

-D name[=value]
--define=name[=value]
-F name[=value]
--force-define=name[=value]

其中每个都等同于“” (请参阅 %define 摘要)。请注意,分隔符是 : 的一部分,对应于 ''、'' 和 ''。%define name valuevalue-Dapi.value.type=union-Dapi.value.type={union}-Dapi.value.type="union"%define api.value.type union%define api.value.type {union}%define api.value.type "union"

Bison 处理多个定义,如下所示:name

  • Bison 悄悄地忽略了所有命令行定义,除了 最后一个。name
  • 如果该命令行定义由 或 指定,则 Bison 报告任何定义的错误 为。-D--define%definename
  • 如果该命令行定义由 or 指定,则 Bison 会悄悄地忽略 的所有定义。-F--force-define%definename
  • 否则,如果 有多个定义,则 Bison 会报告错误。%definename

您应该避免在您的 创建文件,除非您确信悄悄地忽略它是安全的 任何可能添加到语法文件中的冲突。-F--force-define%define

-L language
--language=language

为生成的解析器指定编程语言,就像已指定一样(请参阅 Bison 声明摘要)。目前支持 语言包括C,C++,D和Java。 不区分大小写。%languagelanguage

--locations

假装已指定。参见野牛宣言摘要%locations

-p prefix
--name-prefix=prefix

假装已指定(参见 Bison Declaration Summary)。该选项由 POSIX 指定。当POSIX时 兼容性不是必需的,而是 更好的选项(请参阅同一程序中的多个解析器)。%name-prefix "prefix"-p-Dapi.prefix=prefix

-l
--no-lines

不要在解析器中放置任何预处理器命令 实现文件。通常,Bison 将它们放在解析器中 实现文件,以便 C 编译器和调试器将 将错误与源文件(语法文件)相关联。此选项 导致它们将错误与解析器实现文件相关联, 将其视为独立的源文件。#line

-S file
--skeleton=file

指定要使用的骨架,类似于(请参阅 Bison Declaration Summary)。%skeleton

如果不包含 ,则为骨架的名称 文件。 如果是这样,则是一个绝对文件名或相对于 当前工作目录。 这类似于大多数 shell 解析命令的方式。file/filefile

-k
--token-table

假装已指定。参见野牛宣言摘要%token-table

-y
--yacc

行为更像是传统命令:yacc

  • 生成不同的诊断(这意味着)。-Wyacc
  • 除了 to 之外,还生成语句 将令牌代码与令牌种类名称相关联。#defineenum
  • 如果定义了环境变量,则生成 和 6 的原型(自 Bison 3.8):POSIXLY_CORRECTyyerroryylex
    int yylex (void);
    void yyerror (const char *);
    

    作为 Bison 扩展,需要 和 的其他参数被纳入 帐户。您可以使用 ''(由 POSIX 指定)或 ''(Bison 扩展名)禁用 的原型。同样,对于 .%pure-parser%locations%lex-param%parse-paramyyerror#define yyerror yyerror#define YYERROR_IS_DECLAREDyylex

  • 模仿 Yacc 的输出文件名约定,以便解析器 实现文件被调用,其他输出为 称为 和 。不要只使用 更改输出文件名,因为它也会触发所有 上述行为改变;而是使用''。y.tab.cy.outputy.tab.h--yacc-o y.tab.c

/ 选项旨在与传统的 Yacc 语法。此选项仅对默认的 C 框架有意义。如果您的语法使用 Bison 扩展,则 Bison 不能 与 Yacc 兼容,即使指定了此选项。-y--yaccyacc.c

因此,以下 shell 脚本可以替换 Yacc 和 Bison 发行版包含这样的脚本,以便与 POSIX:yacc

#! /bin/sh
bison -y "$@"

Previous: , Up: Bison Options [Contents][Index]

9.1.4 输出文件

控制输出的选项。

-H [file]
--header=[file]

假装指定了,即写入一个额外的输出文件 包含语法中定义的标记类型名称的定义,如 以及其他一些声明。参见野牛宣言摘要%header

--defines[=file]

Bison 3.8 之前选项的历史名称。--header

-d

这与 except 不接受参数相同,因为 POSIX Yacc 要求可以 与其他短选项捆绑在一起。--header-dfile-d

-b file-prefix
--file-prefix=prefix

假装已指定,即指定要使用 的前缀 对于所有 Bison 输出文件名。参见野牛宣言摘要%file-prefix

-r things
--report=things

编写一个额外的输出文件,其中包含逗号的详细描述 其中的分离列表:things

state

语法描述、冲突(已解决和未解决)以及 解析器的自动机。

itemset

暗示并增强对自动机的描述 每个状态的完整项目集,而不仅仅是其核心。state

lookahead

暗示并增强对自动机的描述 每个规则的前瞻集。state

solved

意味 着。解释冲突是如何解决的,这要归功于 优先级和关联性指令。state

counterexamples
cex

寻找冲突的反例。请参阅反例的生成。 反例需要时间来计算。该选项应为 开发人员在处理语法时使用;这几乎没有意义 在 CI 中使用它。-rcex

all

启用所有项目。

none

不要生成报告。

--report-file=file

指定详细描述。file

-v
--verbose

假装已指定,即写入额外的输出 包含详细语法描述的文件和 解析 器。参见野牛宣言摘要%verbose

-o file
--output=file

指定 for 分析器实现文件。file

其他输出文件的名称由 在 AND 选项下进行了描述。file-v-d

-g [file]
--graph[=file]

输出解析器自动机的图形表示,计算公式为 Bison,Graphviz DOT 格式。 是可选的。如果省略且语法文件为 ,则输出文件将为 。filefoo.yfoo.gv

-x [file]
--xml[=file]

输出由 Bison 计算的解析器自动机的 XML 报告。 是可选的。 如果省略且语法文件为 ,则输出文件将为 。filefoo.yfoo.xml

-M old=new
--file-prefix-map=old=new

在输出中写入文件路径时将前缀替换为 文件。oldnew


Next: , Previous: , Up: Ininvokeking Bison [Contents][Index]

9.2 选项交叉键

以下是按长选项字母顺序排列的选项列表,以帮助您找到 相应的空头期权和指令。

多头期权空头期权野牛指令
--color[=when]
--debug-t%debug
--define=name[=value]-D name[=value]%define name [value]
--feature[=features]-f [features]
--file-prefix-map=old=new-M old=new
--file-prefix=prefix-b prefix%file-prefix "prefix"
--force-define=name[=value]-F name[=value]%define name [value]
--graph[=file]-g [file]
--header=[file]-H [file]%header ["file"]
--help-h
--html[=file]
--language=language-L language%language "language"
--locations%locations
--name-prefix=prefix-p prefix%name-prefix "prefix"
--no-lines-l%no-lines
--output=file-o file%output "file"
--print-datadir
--print-localedir
--report-file=file
--report=things-r things
--skeleton=file-S file%skeleton "file"
--style=file
--token-table-k%token-table
--update-u
--verbose-v%verbose
--version-V
--warnings[=category]-W [category]
--xml[=file]-x [file]
--yacc-y%yacc

上一篇: ,向上: 调用野牛 [内容][索引]

9.3 Yacc 库

Yacc 库包含 and 函数的默认实现。这些默认实现通常不是 有用,但 POSIX 需要它们。要使用 Yacc 库,请链接您的程序 替换为选项。请注意,Bison 对 Yacc 的实现 库是根据 GNU 通用公共许可证的条款分发的 (参见 GNU 通用公共许可证)。yyerrormain-ly

如果使用 Yacc 库的函数,则应声明如下:yyerroryyerror

int yyerror (char const *);

这将被忽略。intyyerror

Yacc 库函数的实现是:main

int main (void)
{
  setlocale (LC_ALL, "");
  return yyparse ();
}

因此,如果您使用它,则会启用国际化支持(例如,错误 消息被翻译),并且您的函数应具有 以下类型签名:yyparse

int yyparse (void);

下一篇: ,上一篇:,上一篇:野牛 [内容][索引]

10 个用其他语言编写的解析器

除了 C 之外,Bison 还可以用 C++、D 和 Java 生成解析器。本章 致力于这些语言。读者应该了解如何 野牛作品;如果您不这样做,请先阅读介绍性章节。


10.1 C++ 解析器

C++ 中的 Bison 解析器是一个对象,是类的实例。yy::parser


下一篇: ,上一篇: C++ 解析器 [内容][索引]

10.1.1 一个简单的 C++ 示例

本教程关于 C++ 解析器基于一个简单的、独立的 例。7 以下各节是参考 带有 C++ 的 Bison 手册,最后一个显示了一个完整的示例 (请参阅完整的 C++ 示例)。

为了看起来更好,我们的示例将采用 C++ 14。不是必需的:野牛 支持原始 C++ 98 标准。

Bison 文件由三个部分组成。在第一部分,序幕中,我们首先 确保我们运行一个足够新的 Bison 版本,并且我们 生成 C++。

%require "3.2"
%language "c++"

让我们直接进入中间部分:语法。我们的输入是 简单的字符串列表,我们在解析完成后显示。

%%
result:
  list  { std::cout << $1 << '\n'; }
;
%nterm <std::vector<std::string>> list;
list:
  %empty     { /* Generates an empty string list */ }
| list item  { $$ = $1; $$.push_back ($2); }
;

我们使用字符串向量作为语义值!使用真正的 C++ 对象 作为语义值——而不仅仅是 POD——我们不能依赖 Bison 的并集 默认情况下使用来存储它们,我们需要变体(请参阅 C++ 变体):

%define api.value.type variant

显然,规则需要打印字符串的向量。 在序言中,我们补充道:result

%code
{
  // Print a list of strings.
  auto
  operator<< (std::ostream& o, const std::vector<std::string>& ss)
    -> std::ostream&
  {
    o << '{';
    const char *sep = "";
    for (const auto& s: ss)
      {
        o << sep << s;
        sep = ", ";
      }
    return o << '}';
  }
}

您可能希望将其移动到命名空间中,以避免将其泄漏 默认命名空间。我们建议您保持操作简单,并且 将详细信息移动到辅助函数中,就像我们对 .yyoperator<<

我们的字符串列表将由两种类型的项目构建:数字和 字符串:

%nterm <std::string> item;
%token <std::string> TEXT;
%token <int> NUMBER;
item:
  TEXT
| NUMBER  { $$ = std::to_string ($1); }
;

在 的情况下,隐式默认操作适用:。TEXT$$ = $1


我们的扫描仪值得关注。传统的接口不是类型安全的:因为令牌种类和令牌值是 不相关,您可以返回带有字符串作为语义的 A 价值。为了避免这种情况,我们使用令牌构造函数(请参阅完整符号)。该指令:yylexNUMBER

%define api.token.constructor

请求 Bison 生成函数 和 ,但也生成 ,用于输入的结尾。make_TEXTmake_NUMBERmake_YYEOF

我们的扫描仪一切就绪:

%code
{
  namespace yy
  {
    // Return the next token.
    auto yylex () -> parser::symbol_type
    {
      static int count = 0;
      switch (int stage = count++)
        {
        case 0:
          return parser::make_TEXT ("I have three numbers for you.");
        case 1: case 2: case 3:
          return parser::make_NUMBER (stage);
        case 4:
          return parser::make_TEXT ("And that's all!");
        default:
          return parser::make_YYEOF ();
        }
    }
  }
}

在结语中,野牛语法文件的第三部分,我们留下了简单的 详细信息:错误报告功能和 main 功能。

%%
namespace yy
{
  // Report an error to the user.
  auto parser::error (const std::string& msg) -> void
  {
    std::cerr << msg << '\n';
  }
}

int main ()
{
  yy::parser parse;
  return parse ();
}

编译并运行!

$ bison simple.yy -o simple.cc
$ g++ -std=c++14 simple.cc -o simple
$ ./simple
{I have three numbers for you., 1, 2, 3, And that's all!}

下一篇: , 上一篇: , 上一篇: C++ 解析器 [内容][索引]

10.1.2 C++ 野牛接口

C++ 确定性解析器是使用 skeleton 指令选择的, ‘’.参见野牛宣言摘要%skeleton "lalr1.cc"

运行时,将在 '' 中创建多个实体 命名空间。使用 '' 指令更改命名空间名称, 请参阅 %define 摘要。生成各种类 在以下文件中:bisonyy%define api.namespace

file.hh

(假设语法文件的扩展名为“”。这 C++ 分析器类和辅助类型的声明。默认情况下,这 文件未生成(请参阅 Bison 声明摘要)。.yy

file.cc

C++ 分析器类的实现。的基名和扩展名 这两个文件 ( 和 ) 遵循 与常规 C 解析器相同的规则(请参阅调用 Bison)。file.hhfile.cc

location.hh

当两者都启用时生成,这 file 包含用于位置跟踪的类 和 的定义。如果出现以下情况,则不会生成它 “”是指定的,或者如果用户定义 使用位置。请参阅 C++ 位置值%header%locationspositionlocation%define api.location.file none

position.hh
stack.hh

无用的遗留文件。要摆脱它,请使用“”或 新。%require "3.2"

所有这些文件都使用 Doxygen 进行记录;为一个 完整准确的文档。doxygen


下一篇: , 上一篇: , 上一篇: C++ 解析器 [内容][索引]

10.1.3 C++ 解析器接口

输出文件并声明和 在命名空间中定义解析器类。类名默认值 更改为 ,但可以使用 '' 进行更改。下面详细介绍了此类的接口。它可以是 使用该功能扩展:其语义略有 更改,因为它描述了 Parser 类的附加成员,并且 其构造函数的附加参数。file.hhfile.ccyyparser%define api.parser.class {name}%parse-param

解析器类型:token

包含(仅)枚举的结构, 它定义了令牌。要引用令牌,请使用 。扫描程序可以使用 '' 来“导入”令牌枚举(请参阅 Calc++ 扫描程序)。token_kind_typeFOOyy::parser::token::FOOtypedef yy::parser::token token;

解析器类型:token_kind_type

令牌种类的枚举。它的枚举器是从 令牌名称,可能具有令牌前缀 (参见 api.token.prefix):

/// Token kinds.
struct token
{
  enum token_kind_type
  {
    YYEMPTY = -2,              // No token.
    YYEOF = 0,                 // "end of file"
    YYerror = 256,             // error
    YYUNDEF = 257,             // "invalid token"
    PLUS = 258,                // "+"
    MINUS = 259,               // "-"
    [...]
    VAR = 271,                 // "variable"
    NEG = 272                  // NEG
  };
};

/// Token kind, as returned by yylex.
typedef token::token_kind_type token_kind_type;
解析器类型:value_type

语义值的类型。请参阅 C++ 语义值

解析器类型:location_type

位置的类型(如果启用了位置跟踪)。请参阅 C++ 位置值

解析器类型:syntax_error

此类派生自 。抛出它的实例 从扫描程序或从引发分析错误的操作。这是 等同于首次调用以报告位置和 消息的语法错误,然后调用输入 错误恢复模式。但与之相反,只能是 从用户操作(即写入操作本身)调用, 可以从从用户操作调用的函数中引发异常。std::runtime_errorerrorYYERRORYYERROR

解析器上的构造函数: parser ()
解析器上的构造函数: parser type1 arg1, ...)

生成新的分析器对象。没有争论,除非 '' 被使用。%parse-param {type1 arg1}

syntax_error上的构造函数: syntax_error const location_type& lconst std::string& m
syntax_error上的构造函数: syntax_error const std::string& m

实例化语法错误异常。

解析器上的方法:int operator() ()
解析器上的方法:int parse ()

运行句法分析,成功时返回 0,否则返回 1。双 例程是等效的,更像 C++。operator()

整个函数被包装在一个 / 块中,因此 当抛出异常时,将调用 S 来释放 前瞻符号,以及推送到堆栈上的符号。trycatch%destructor

生成的解析器中与异常相关的代码受 CPP 防护保护 () 并在不支持异常时禁用(即传递给 C++ 编译器)。#if-fno-exceptions

解析器上的方法:std::ostream&debug_stream ()
解析器上的方法:void set_debug_stream std::ostream&o)

获取或设置用于跟踪分析的流。它默认为 。std::cerr

解析器方法: debug_level_type debug_level ()
解析器方法:void set_debug_level (debug_level_type l

获取或设置跟踪级别(积分)。目前它的值是 0,无跟踪,或非零,完全跟踪。

解析器上的方法:void error const location_type& lconst std::string& m
解析器上的方法:void error const std::string& m

此成员函数的定义必须由用户提供: Parser 使用它来报告在 处发生的解析器错误,如 所述。如果未启用位置跟踪,则使用第二个签名。lm


下一篇: , 上一篇: , 上一篇: C++ 解析器 [内容][索引]

10.1.4 C++语义值

Bison 支持两种不同的方法来处理 C++ 中的语义值。一是 与 C 接口类似,并且依赖于联合。正如 C++ 从业者所知道的, 联合在 C++ 中很不方便,因此提供了另一种方法, 基于变体。


下一篇: , 向上: C++ 语义值 [内容][索引]

10.1.4.1 C++ 联合

该指令适用于 C,请参阅联盟宣言。在 特别是它产生一个真正的,它有几个特定的 C++ 中的功能。%unionunion

  • - 值类型为 ,而不是 。yy::parser::value_typeYYSTYPE
  • - 不能使用非 POD(纯旧数据)类型。C++ 禁止任何实例 在联合中具有构造函数的类:仅指向此类对象的指针 是允许的。C++11放宽了这一限制,但以安全为代价。

因为对象必须通过指针存储,所以内存不是 自动回收:使用指令是 只有避免泄漏的手段。请参阅释放丢弃的符号%destructor


上一篇: , 上一篇: C++ 语义值 [内容][索引]

10.1.4.2 C++ 变体

Bison 提供了基于变体的语义值实现 C++。这缓解了上一节中报告的所有限制, 特别是,可以在没有指针的情况下使用对象类型。

若要启用基于变体的语义值,请将变量设置为(请参阅 %define 摘要)。然后被忽略;不要使用 的字段名称来“键入”符号,而是使用真正的类型。%defineapi.value.typevariant%union%union

例如,而不是:

%union
{
  int ival;
  std::string* sval;
}
%token <ival> NUMBER;
%token <sval> STRING;

写:

%token <int> NUMBER;
%token <std::string> STRING;

STRING不再是指针,这应该相当简化用户 语法和扫描仪(特别是内存)中的操作 管理)。

由于 C++ 具有析构函数,并且习惯上专门支持统一打印值,因此变体也 通常简化 Bison 打印机和析构函数。operator<<

变体比工会更严格。当基于工会时,您可以玩任何 肮脏的游戏,比如说存储一个,读取一个,然后把一个存储在里面。这不再是 变体可能:它们必须初始化,然后分配给,并且 最终,被摧毁。事实上,野牛变种禁止使用 替代类型,例如“”或“”,甚至 在 midrule 操作中。必须使用类型化的中间规则操作 (请参阅类型化 Midrule 操作)。yylvalintchar*double$<int>2$<std::string>$

value_type的方法:T&emplace<T> ()
value_type方法:T&emplace<T>const T&t

仅在 C++ 98/C++03 中可用。默认构造/复制构造来自 .返回对实际值可能存储位置的引用。 要求尚未初始化变体。t

value_type方法:T&emplace<T,U>U&&... u

仅在 C++11 及更高版本中可用。从以下位置构建类型的变体 可变参数转发引用 。Tu...

警告:我们不使用 Boost.Variant,原因有两个。首先,它 在用户机器上要求 Boost 似乎是不可接受的(即, 生成的解析器将在其上编译的机器,而不是 已运行)。其次,对于每个可能的语义值, Boost.Variant 不仅存储值,还存储一个指定其 类型。但是解析器已经“知道”语义值的类型,所以 这将是重复信息。bison

我们也不使用 C++ 17:我们希望支持所有 C++ 标准,当然也存储要记录的标签 当前类型。std::variantstd::variant

因此,我们开发了类型标签为外部的轻量级变体(所以 他们实际上真的很像C++)。有许多 变体(当前实现)的局限性:unions

  • 必须强制对齐:值应根据 最苛刻的类型。计算尽可能小的对齐需要 目前未在 Bison 中实现的元编程技术,以及 因此,因为,据我们所知,是最苛刻的 在所有平台上键入,对任何内容都强制对齐 实际使用类型。在某些情况下,这可能会浪费空间。doubledouble
  • 可能存在我们不知道的可移植性问题。

据我们所知,这些限制是可以缓解的。一切所需 是一些时间和/或一些有才华的C++黑客愿意为Bison做出贡献。


下一篇: , 上一篇: , 上一篇: C++ 解析器 [内容][索引]

10.1.5 C++ 位置值

使用该指令时,C++ 解析器支持 位置跟踪,请参阅跟踪位置%locations

默认情况下,两个辅助类定义一个 ,一个点 在文件中,和 ,由一对 S 组成的范围(可能跨越多个文件)。如果定义了变量,则这些类将不会 生成,并将使用用户定义的类型。positionlocationposition%defineapi.location.type


下一篇: , 上一篇: C++ 位置值 [内容][索引]

10.1.5.1 C++position

职位类型:filename_type

文件名的基本类型。默认值为 。 请参阅 api.filename.type,以更改其定义。const std::string

职位类型:counter_type

用于存储行号和列号的类型。定义为 .int

位置构造函数: position filename_type* file = nullptr, counter_type line = 1, counter_type col = 1)

创建一个表示给定点的点。请注意,这是 销毁时未回收:内存管理必须 在其他地方处理。positionfileposition

位置方法:void initialize filename_type* file = nullptr, counter_type line = 1, counter_type col = 1)

将位置重置为给定值。

位置的实例变量:filename_type* 文件

文件的名称。它将始终作为指针(解析器)处理 永远不会复制或取消分配它。

位置的实例变量:counter_type

该行,从 1 开始。

位置方法:隙线counter_type = 1)

如果不是 null,则按行前进,重置 列号。生成的行号不能小于 1。heightheight

位置的实例变量:counter_type

该列,从 1 开始。

位置方法:void columns counter_type width = 1)

按列前进,不更改行号。这 生成的列号不能小于 1。width

位置方法:position& operator+= counter_type width
位置方法:位置运算符+counter_type宽度
位置方法:position& operator-= counter_type width
位置方法:位置运算符-counter_type宽度

各种形式的句法糖。columns

位置方法:bool operator== const position& that
位置方法:bool operator!= const position& that

是否 和 表示相等/不同的位置。*thisthat

函数: std::ostream& operator<< std::ostream& oconst position& p

报告如下: '',或 '' 如果为 null。pofile:line.columnline.columnfile


下一篇: , 上一篇: , 上一篇: C++ 位置值 [内容][索引]

10.1.5.2 C++location

位置构造函数: location const position& beginconst position& end

从范围的终结点创建一个。Location

位置上的构造函数: location const position& pos = position())
位置构造函数: location filename_type* 文件counter_typecounter_type

创建一个表示位于给定点的空范围。Location

位置方法:void initialize filename_type* file = nullptr, counter_type line = 1, counter_type col = 1)

在给定值处将位置重置为空范围。

位置的实例变量:position begin
位置的实例变量:position end

范围的第一个(包括)位置,以及第一个超越的位置。

定位方法:void columns counter_type width = 1)
定位方法:隙线counter_type高度=1)

转发到该位置。end

位置方法:location operator+counter_type宽度
位置方法:location operator+= counter_type宽度
位置方法:location operator-counter_type宽度
位置方法:location operator-= counter_type宽度

各种形式的句法糖。columns

位置方法:location operator+ const location& end
位置方法:location operator+= const location& end

连接两个位置:从第一个位置开始,到 第二个的位置。

位置方法:void step ()

转到 。beginend

位置方法:bool operator== const location& that
位置方法:bool operator!= const location&that

是否 和 表示相等/不同范围 位置。*thisthat

函数: std::ostream& operator<< std::ostream& oconst location& p

报告,处理特殊情况,例如:未定义或相等的文件名/行或列。pofilename


下一篇: , 上一篇: , 上一篇: C++ 位置值 [内容][索引]

10.1.5.3 公开位置类

当两者都启用时,Bison 将生成 附加文件:.如果您不使用外部位置 ,您可以避免使用 '' 创建它。%header%locationslocation.hh%define api.location.file none

但是,例如,如果您的解析器构建了一个摘要,则此文件很有用 用位置装饰的语法树:您可以独立于 Bison 的解析器使用 Bison 的类型。您可以以不同的方式命名文件, 例如,“'”:这个名字 可以有目录组件,甚至可以是绝对的。位置的方式 文件包含在内,由 控制。location%define api.location.file "include/ast/location.hh"api.location.include

这样,就可以让多个解析器共享同一个位置 文件。

例如,在 中生成文件:src/foo/parser.yyinclude/ast/loc.hh

// src/foo/parser.yy
%locations
%define api.namespace {foo}
%define api.location.file "include/ast/loc.hh"
%define api.location.include {<ast/loc.hh>}

并将其用于:src/bar/parser.yy

// src/bar/parser.yy
%locations
%define api.namespace {bar}
%code requires {#include <ast/loc.hh>}
%define api.location.type {bar::location}

支持绝对文件名;它是安全的 将标志传递给 for 。生成的文件不会 引用此绝对路径,感谢 ''。对于编译器来说,将“”添加到您的遗嘱中就足够了 找到。Makefile-Dapi.location.file='"$(top_srcdir)/include/ast/loc.hh"'bisonsrc/foo/parser.yy%define api.location.include {<ast/loc.hh>}-I $(top_srcdir)/includeCPPFLAGSast/loc.hh


上一篇: ,上一篇: C++ 位置值 [内容][索引]]

10.1.5.4 用户定义的位置类型

您可以使用变量来指定自己的类型,而不是使用内置类型:%defineapi.location.type

%define api.location.type {LocationType}

您的要求是:LocationType

  • 它必须是可复制的;
  • 为了计算 in a reduction的(默认)值, 解析器基本上可以运行@$
    @$.begin = @1.begin;
    @$.end   = @N.end; // The location of last right-hand side symbol.
    

    所以必须有可复制的和成员;beginend

  • 或者,您可以重新定义默认位置的计算,在 在这种情况下,这些成员不是必需的(请参阅位置的默认操作);
  • 如果启用了跟踪,则必须存在“”函数。std::ostream& operator<< (std::ostream& o, const LocationType& s)

在具有多个 C++ 解析器的程序中,还可以使用该变量共享一组通用的内置变量 和 的定义。例如,一个 解析器可能使用:%defineapi.location.typepositionlocationmaster/parser.yy

%header
%locations
%define api.namespace {master::}

生成 AND 文件,供其他解析器重用,如下所示:master/position.hhmaster/location.hh

%define api.location.type {master::location}
%code requires { #include <master/location.hh> }

下一篇: , 上一篇: , 上一篇: C++ 解析器 [内容][索引]

10.1.6 C++ 解析器上下文

使用“”时(请参阅语法错误报告函数 yyreport_syntax_error),用户必须定义以下函数。%define parse.error custom

解析器方法:void report_syntax_error const context_type&ctxconst

向用户报告语法错误。是否使用取决于 用户。yyerror

使用以下类型和函数生成错误消息。

解析器类型:上下文

捕获语法错误情况的类型。

解析器类型:symbol_kind_type

所有语法符号、标记和非终端的枚举。其 枚举器是从符号名称伪造的:

struct symbol_kind
{
  enum symbol_kind_type
  {
    S_YYEMPTY = -2,      // No symbol.
    S_YYEOF = 0,         // "end of file"
    S_YYERROR = 1,       // error
    S_YYUNDEF = 2,       // "invalid token"
    S_PLUS = 3,          // "+"
    S_MINUS = 4,         // "-"
    [...]
    S_VAR = 14,          // "variable"
    S_NEG = 15,          // NEG
    S_YYACCEPT = 16,     // $accept
    S_exp = 17,          // exp
    S_input = 18         // input
  };
};
typedef symbol_kind::symbol_kind_t symbol_kind_type;
上下文方法:const symbol_type& lookahead () const

“意外”标记:导致语法错误的前瞻。

上下文方法:symbol_kind_type token () const

导致语法错误的 lookahead 标记的符号类型。如果没有前瞻,则返回。symbol_kind::S_YYEMPTY

上下文方法:const location& location () const

语法错误的位置(前瞻错误的位置)。

上下文方法:int expected_tokens symbol_kind_type argv[]int argcconst

填充预期的标记,其中从不包含 、 或 。argvsymbol_kind::S_YYEMPTYsymbol_kind::S_YYERRORsymbol_kind::S_YYUNDEF

永远不要把更多的元素放进去,在成功上 返回存储在 中的令牌数。如果还有更多 预期的令牌比 ,填满并返回 0. 如果没有预期的令牌,则也返回 0,但设置为 。argcargvargvargcargvargcargv[0]symbol_kind::S_YYEMPTY

如果为 null,则返回存储所有可能文件所需的大小 值,它总是小于 。argvYYNTOKENS

解析器上的方法:const char * symbol_name symbol_kind_t符号const

其种类为 的符号的名称,可能已翻译。symbol

返回 when is 。std::stringparse.errorverbose

自定义语法错误函数如下所示。此实现是 不适合国际化,请参阅示例以获取更好的替代方案。c/bistromathic

void
yy::parser::report_syntax_error (const context& ctx)
{
  int res = 0;
  std::cerr << ctx.location () << ": syntax error";
  // Report the tokens expected at this point.
  {
    enum { TOKENMAX = 5 };
    symbol_kind_type expected[TOKENMAX];
    int n = ctx.expected_tokens (ctx, expected, TOKENMAX);
    for (int i = 0; i < n; ++i)
      std::cerr << i == 0 ? ": expected " : " or "
                << symbol_name (expected[i]);
  }
  // Report the unexpected token.
  {
    symbol_kind_type lookahead = ctx.token ();
    if (lookahead != symbol_kind::S_YYEMPTY)
      std::cerr << " before " << symbol_name (lookahead));
  }
  std::cerr << '\n';
}

您仍然必须提供一个函数,例如用于 报告内存耗尽。yyerror


下一篇: , 上一篇: , 上一篇: C++ 解析器 [内容][索引]

10.1.7 C++ 扫描仪界面

解析器通过调用 来调用扫描程序。与 C 相反 解析器,C++ 解析器始终是纯的:使用 '' 指令。实际的接口取决于您使用的是联合还是变体。yylex%define api.pureyylex


下一篇: , 上一篇: C++ 扫描仪界面 [内容][索引]

10.1.7.1 拆分符号

生成的解析器应具有以下原型。yylex

函数: int yylex value_type* yylvallocation_type* yylloctype1 arg1, ...)
函数: int yylex value_type* yylvaltype1 arg1, ...)

返回下一个令牌。它的种类是返回值、语义值和 location(如果启用)是 和 。调用 '' 产生额外的参数。yylvalyylloc%lex-param {type1 arg1}

请注意,使用变体时,接口是相同的, 但处理方式不同。yylexyylval

Lex 扫描程序中基于联合的常规代码通常如下所示:

[0-9]+   {
           yylval->ival = text_to_int (yytext);
           return yy::parser::token::INTEGER;
         }
[a-z]+   {
           yylval->sval = new std::string (yytext);
           return yy::parser::token::IDENTIFIER;
         }

使用变体,已经构建,但尚未构建 初始 化。因此,代码如下所示:yylval

[0-9]+   {
           yylval->emplace<int> () = text_to_int (yytext);
           return yy::parser::token::INTEGER;
         }
[a-z]+   {
           yylval->emplace<std::string> () = yytext;
           return yy::parser::token::IDENTIFIER;
         }

[0-9]+   {
           yylval->emplace (text_to_int (yytext));
           return yy::parser::token::INTEGER;
         }
[a-z]+   {
           yylval->emplace (yytext);
           return yy::parser::token::IDENTIFIER;
         }

上一篇: , 上一篇: C++ 扫描仪界面 [内容][索引]

10.1.7.2 完整符号

对于 和 ,解析器定义类型 ,并且 期望有以下原型。%define api.value.type variant%define api.token.constructorsymbol_typeyylex

函数:parser::symbol_type yylex ()
函数: parser::symbol_type yylex type1 arg1, ...)

返回一个完整的符号,聚合其类型(即传统的 返回的值),其语义值,可能还有其 位置。调用 '' yield 其他参数。yylex%lex-param {type1 arg1}

解析器类型:symbol_type

一个“完整的符号”,将其种类、价值和(当 适用)位置。

symbol_type 方法:symbol_kind_type kind () const

这个符号的那种。

symbol_type的方法:const char * name () const

此符号的种类的名称。

返回 when is 。std::stringparse.errorverbose


对于每种令牌类型,Bison 都会按如下方式生成命名构造函数。

解析器上的构造函数::symbol_type: symbol_type int tokenconst value_type& valueconst location_type& location
parser::symbol_type上的构造函数: symbol_type int tokenconst location_type& location
解析器上的构造函数::symbol_type: symbol_type int tokenconst value_type& value
parser::symbol_type上的构造函数: symbol_type int token

为令牌类型(包括 ),其语义值(如果有的话)就足够了。通过 iff 位置跟踪已启用。tokenapi.token.prefixvaluevalue_typelocation

和 之间的一致性通过 .tokenvalue_typeassert

例如,给定以下声明:

%define api.token.prefix {TOK_}
%token <std::string> IDENTIFIER;
%token <int> INTEGER;
%token ':';

您可以使用这些构造函数:

symbol_type (int token, const std::string&, const location_type&);
symbol_type (int token, const int&, const location_type&);
symbol_type (int token, const location_type&);

通过以下方式检查令牌类型和值类型之间的正确匹配;例如,“'”将中止。叫 构造函数是可取的(见下文),因为它们提供了更好的类型安全性 (例如,“”甚至不会编译),但symbol_type 当在运行时发现令牌类型时,构造函数可能会有所帮助,例如,assertsymbol_type (ID, 42)make_ID (42)

[a-z]+   {
           if (auto i = lookup_keyword (yytext))
             return yy::parser::symbol_type (i, loc);
           else
             return yy::parser::make_ID (yytext, loc);
         }

请注意,可能会生成和编译类型不正确的代码 (例如'')。它将在运行时失败, 前提是启用了断言(即未传递 到编译器)。Bison 支持保证该类型的替代方案 不正确的代码甚至无法编译。事实上,它生成了名为 构造函数如下。symbol_type (':', yytext, loc)-DNDEBUG

解析器上的方法:symbol_type make_token const value_type& valueconst location_type& location
解析器上的方法:symbol_type make_token const location_type& location
解析器上的方法:symbol_type make_token const value_type& value
解析器上的方法:symbol_type make_token ()

为令牌类型构建一个完整的终端符号(不是 包括 ),其语义值,如果它有, 是足够的.通过 iff 位置跟踪已启用。tokenapi.token.prefixvaluevalue_typelocation

例如,给定以下声明:

%define api.token.prefix {TOK_}
%token <std::string> IDENTIFIER;
%token <int> INTEGER;
%token COLON;
%token EOF 0;

Bison 生成:

symbol_type make_IDENTIFIER (const std::string&, const location_type&);
symbol_type make_INTEGER (const int&, const location_type&);
symbol_type make_COLON (const location_type&);
symbol_type make_EOF (const location_type&);

应在扫描仪中使用,如下所示。

[a-z]+   return yy::parser::make_IDENTIFIER (yytext, loc);
[0-9]+   return yy::parser::make_INTEGER (text_to_int (yytext), loc);
":"      return yy::parser::make_COLON (loc);
<<EOF>>  return yy::parser::make_EOF (loc);

没有标识符的令牌是不可访问的:你不能简单地 使用诸如 ,它们必须声明为 , 包括文件末尾令牌。':'%token


上一篇: , 上一篇: C++ 解析器 [内容][索引]

10.1.8 一个完整的 C++ 示例

本节演示如何使用 C++ 解析器,该解析器具有简单但完整的 例。这个例子应该在你的系统上可用,可以编译了, 在目录中。它专注于 使用Bison,因此各种C++类的设计非常 幼稚:没有访问器,没有成员封装等。我们将使用 Lex 扫描仪,更准确地说,是 Flex 扫描仪,用于演示各种 相互 作用。手写扫描仪实际上更容易使用。examples/c++/calc++


10.1.8.1 Calc++ — C++ 计算器

当然,语法是专门用于算术的,一个单一的表达式, 可能前面有变量赋值。包含以下内容的环境 可能是预定义的变量,例如 和 ,是 与解析器交换。下面是有效输入的示例。onetwo

three := 3
seven := one + two * three
seven * seven

下一篇: , 上一篇: , 上一篇: 完整的 C++ 示例 [内容][索引]

10.1.8.2 Calc++ 解析驱动程序

为了支持与解析器(和扫描仪)的纯接口,该技术 的“解析上下文”很方便:一个包含所有 要交换的数据。因为,除了简单地启动解析之外,还有 是要执行的几个辅助任务(打开文件进行扫描, 实例化解析器等),我们建议转换简单解析 上下文结构转换为一个成熟的解析驱动程序类。

此驱动程序类在 中的声明如下。 第一部分包括 CPP 防护装置并导入所需的标准 库组件,以及 Parser 类的声明。driver.hh

#ifndef DRIVER_HH
# define DRIVER_HH
# include <string>
# include <map>
# include "parser.hh"

然后是扫描功能的声明。伟创力希望 的签名 要在宏中定义,并且 C++ 分析器期望声明它。我们可以将两者考虑如下。yylexYY_DECL

// Give Flex the prototype of yylex we want ...
# define YY_DECL \
  yy::parser::symbol_type yylex (driver& drv)
// ... and declare it for the parser's sake.
YY_DECL;

然后,使用其最明显的成员声明该类。driver

// Conducting the whole scanning and parsing of Calc++.
class driver
{
public:
  driver ();

  std::map<std::string, int> variables;

  int result;

主要例程当然是调用解析器。

  // Run the parser on file F.  Return 0 on success.
  int parse (const std::string& f);
  // The name of the file being parsed.
  std::string file;
  // Whether to generate parser debug traces.
  bool trace_parsing;

要封装与 Flex 扫描仪的协调,具有 成员函数打开和关闭扫描阶段。

  // Handling the scanner.
  void scan_begin ();
  void scan_end ();
  // Whether to generate scanner debug traces.
  bool trace_scanning;
  // The token's location used by the scanner.
  yy::location location;
};
#endif // ! DRIVER_HH

驱动程序 () 的实现很简单。driver.cc

#include "driver.hh"
#include "parser.hh"

driver::driver ()
  : trace_parsing (false), trace_scanning (false)
{
  variables["one"] = 1;
  variables["two"] = 2;
}

成员函数值得关注。parse

int
driver::parse (const std::string &f)
{
  file = f;
  location.initialize (&file);
  scan_begin ();
  yy::parser parse (*this);
  parse.set_debug_level (trace_parsing);
  int res = parse ();
  scan_end ();
  return res;
}

下一篇: , Previous: , Up: 完整的 C++ 示例 [内容][索引]

10.1.8.3 calc++ 解析器

语法文件首先询问 C++ 确定性 解析器框架,创建解析器头文件。因为 C++ 骨架换了好几次,需要你的版本更安全 设计了语法。parser.yy

%skeleton "lalr1.cc" // -*- C++ -*-
%require "3.8.1"
%header

因为我们的扫描仪只返回真正的令牌,从不返回简单的字符 (即,它返回 '',而不是 ''),我们可以避免转换。PLUS'+'

%define api.token.raw

此示例使用真正的 C++ 对象作为语义值,因此,我们 需要基于变体的语义值存储。确保我们 正确使用它,我们启用断言。充分受益于型式安全 而更自然的“符号”定义,我们启用.api.token.constructor

%define api.token.constructor
%define api.value.type variant
%define parse.assert

然后是语义值所需的声明/包含。 因为解析器使用解析驱动程序,并且相互,所以两者都希望 包括另一个的标题,这当然是疯狂的。这 将使用转发声明打破相互依赖关系。因为 驱动程序的标头需要有关分析器类的详细信息(在 特别是它的内部类型),它是解析器的标头,它将使用 驱动程序的转发声明。请参见%code 摘要

%code requires {
  # include <string>
  class driver;
}

驱动程序通过引用分析器和扫描程序传递。 这提供了一个简单但有效的纯接口,不依赖于 全局变量。

// The parsing context.
%param { driver& drv }

然后我们请求位置跟踪。

%locations

使用以下两个指令启用解析器跟踪和详细错误 消息。但是,详细的错误消息可能包含不正确的内容 未启用前瞻校正时的信息(请参阅 LAC)。

%define parse.trace
%define parse.error detailed
%define parse.lac full

“”和“”之间的代码在文件中输出;它需要有关驱动程序的详细信息。%code {}*.cc

%code {
# include "driver.hh"
}

为每个符号提供了用户友好的名称。为了避免名称冲突,请执行以下操作 生成的文件(请参阅 Calc++ 扫描程序),前缀标记(请参阅 %define Summary)。TOK_

%define api.token.prefix {TOK_}
%token
  ASSIGN  ":="
  MINUS   "-"
  PLUS    "+"
  STAR    "*"
  SLASH   "/"
  LPAREN  "("
  RPAREN  ")"
;

由于我们使用基于变体的语义值,因此不使用 和 ,并且期望真正的类型,而不是类型 标签。%union%token%nterm%type

%token <std::string> IDENTIFIER "identifier"
%token <int> NUMBER "number"
%nterm <int> exp

在错误期间启用内存释放不需要 恢复;例如,字符串的内存将由 常规析构函数。所有值都使用它们打印(请参阅打印语义值)。%destructoroperator<<

%printer { yyo << $$; } <*>;

语法本身很简单(参见位置跟踪计算器:ltcalc)。

%%
%start unit;
unit: assignments exp  { drv.result = $2; };

assignments:
  %empty                 {}
| assignments assignment {};

assignment:
  "identifier" ":=" exp { drv.variables[$1] = $3; };

%left "+" "-";
%left "*" "/";
exp:
  "number"
| "identifier"  { $$ = drv.variables[$1]; }
| exp "+" exp   { $$ = $1 + $3; }
| exp "-" exp   { $$ = $1 - $3; }
| exp "*" exp   { $$ = $1 * $3; }
| exp "/" exp   { $$ = $1 / $3; }
| "(" exp ")"   { $$ = $2; }
%%

最后,成员函数报告错误。error

void
yy::parser::error (const location_type& l, const std::string& m)
{
  std::cerr << l << ": " << m << '\n';
}

下一篇: , 上一篇: , 上一篇: 完整的 C++ 示例 [内容][索引]

10.1.8.4 Calc++ 扫描仪

除了标准标头外,Flex 扫描仪还包括驱动程序的 然后解析器获取定义的标记集。

%{ /* -*- C++ -*- */
# include <cerrno>
# include <climits>
# include <cstdlib>
# include <cstring> // strerror
# include <string>
# include "driver.hh"
# include "parser.hh"
%}

由于我们的计算器没有类似功能,因此我们不需要 .我们不需要 和 函数 或者,我们解析一个实际的文件,这不是一个交互式会话 用户。最后,我们启用扫描仪跟踪。#includeyywrapunputinput

%option noyywrap nounput noinput batch debug

以下函数将方便转换表示数字的字符串 变成令牌。NUMBER

%{
  // A number symbol corresponding to the value in S.
  yy::parser::symbol_type
  make_NUMBER (const std::string &s, const yy::parser::location_type& loc);
%}

缩写允许更易读的规则。

id    [a-zA-Z][a-zA-Z_0-9]*
int   [0-9]+
blank [ \t\r]

以下段落足以准确跟踪位置。每次调用时,起始位置都会移动到结束位置。 然后,当匹配模式时,其宽度将添加到结束列中。什么时候 匹配行的末端,调整结束光标,每次空白 匹配时,开始光标将移动到结束光标上以有效忽略 标记前面的空白。评论将得到平等对待。yylex

%{
  // Code run each time a pattern is matched.
  # define YY_USER_ACTION  loc.columns (yyleng);
%}
%%
%{
  // A handy shortcut to the location held by the driver.
  yy::location& loc = drv.location;
  // Code run each time yylex is called.
  loc.step ();
%}
{blank}+   loc.step ();
\n+        loc.lines (yyleng); loc.step ();

规则很简单。驱动程序用于报告错误。

"-"        return yy::parser::make_MINUS  (loc);
"+"        return yy::parser::make_PLUS   (loc);
"*"        return yy::parser::make_STAR   (loc);
"/"        return yy::parser::make_SLASH  (loc);
"("        return yy::parser::make_LPAREN (loc);
")"        return yy::parser::make_RPAREN (loc);
":="       return yy::parser::make_ASSIGN (loc);

{int}      return make_NUMBER (yytext, loc);
{id}       return yy::parser::make_IDENTIFIER (yytext, loc);
.          {
             throw yy::parser::syntax_error
               (loc, "invalid character: " + std::string(yytext));
}
<<EOF>>    return yy::parser::make_YYEOF (loc);
%%

您应该在解析器和扫描程序中保持规则简单。 然后,从辅助函数中抛出可以非常方便地报告错误。

yy::parser::symbol_type
make_NUMBER (const std::string &s, const yy::parser::location_type& loc)
{
  errno = 0;
  long n = strtol (s.c_str(), NULL, 10);
  if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
    throw yy::parser::syntax_error (loc, "integer is out of range: " + s);
  return yy::parser::make_NUMBER ((int) n, loc);
}

最后,因为与扫描程序相关的驱动程序的成员函数依赖于 在扫描仪的数据上,在此文件中实现它们更简单。

void
driver::scan_begin ()
{
  yy_flex_debug = trace_scanning;
  if (file.empty () || file == "-")
    yyin = stdin;
  else if (!(yyin = fopen (file.c_str (), "r")))
    {
      std::cerr << "cannot open " << file << ": " << strerror (errno) << '\n';
      exit (EXIT_FAILURE);
    }
}
void
driver::scan_end ()
{
  fclose (yyin);
}

10.1.8.5 Calc++ 顶层

顶级文件 ,没有问题。calc++.cc

#include <iostream>
#include "driver.hh"

int
main (int argc, char *argv[])
{
  int res = 0;
  driver drv;
  for (int i = 1; i < argc; ++i)
    if (argv[i] == std::string ("-p"))
      drv.trace_parsing = true;
    else if (argv[i] == std::string ("-s"))
      drv.trace_scanning = true;
    else if (!drv.parse (argv[i]))
      std::cout << drv.result << '\n';
    else
      res = 1;
  return res;
}

下一篇: , 上一篇: 器, 上一篇: 用其他语言编写的解析器 [目录][索引]

10.2 D 解析器


下一篇: , 上一篇: D 解析器 [内容][索引]

10.2.1 D 野牛接口

D 解析器骨架是使用指令或 / 选项选择的。%language "D"-L D--language=D

生成 D 解析器时,“' 将创建一个 名为包含 解析器实现。使用不带后缀的语法文件是 目前坏了。解析器实现文件的基名可以是 由指令或 / 选项更改。整个解析器实现 可以通过指令或 / 选项更改文件名。分析器实现文件 包含分析器的单个类。bison basename.ybasename.d.y%file-prefix-b--file-prefix%output-o--output

您可以使用 Ddoc 为生成的解析器创建文档。

D 中目前不支持 GLR 解析器。不要使用该指令。glr-parser

不能为 D 解析器生成头文件。不要使用指令或 / 选项。%header-d--header


下一篇: , 上一篇: , 上一篇: D 解析器 [内容][索引]

10.2.2 D 语义值

语义类型由 和 '' 处理,类似于 C/C++ 解析器。在后一种情况下,并集 值由后端处理。在 D 中,联合可以保存类、结构、 等等,所以这个指令更类似于 C++ 中的“”。%union%define api.value.type union%define api.value.type variant

D 解析器不支持 ,因为 语言 采用垃圾回收。解析器将尝试保存引用 根据需要尽可能短的时间进行语义值。%destructor

D 解析器支持 。类型为 的输出示例,其中 是解析器的调试输出:%printerintyyo

%printer { yyo.write($$); } <int>

下一篇: , 上一篇: , 上一篇: D 解析器 [内容][索引]

10.2.3 D 位置值

使用该指令时,D 解析器支持位置 跟踪,请参阅跟踪位置。位置和位置 提供结构。%locations

位置的实例变量:位置开始
位置的实例变量:位置结束

范围的第一个(包括)位置,以及第一个超越的位置。

位置构造函数: this(Position loc

创建一个表示位于给定点的空范围。Location

位置构造函数: this(Position beginPosition end

从范围的终结点创建一个。Location

位置方法:string toString()

由位置表示为字符串的范围。


下一篇: , 上一篇: , 上一篇: D 解析器 [内容][索引]

10.2.4 D 解析器接口

生成的分析器类的名称默认为 。可以使用“”更改前缀。 或者,使用 '' 给出一个 类的自定义名称。下面详细介绍了此类的接口。YYParserYY%define api.prefix%define api.parser.class {name}

默认情况下,分析器类具有公共可见性。要向 Parser 类、 和/或 。%defineapi.parser.publicapi.parser.abstractapi.parser.final

解析器类的超类和实现的接口可以是 使用 '' 和 '' 指令指定。%define api.parser.extends%define api.parser.implements

解析器类定义一个接口(请参阅 D 扫描仪接口)。除了此接口和 接口,所有其他成员和字段前面都带有 OR 前缀,以避免与用户代码发生冲突。LexeryyYY

可以使用该指令扩展解析器类。默认情况下,该指令的每次出现都会添加一个 public 字段添加到解析器类,以及指向其构造函数的参数,该参数 自动初始化它们。%parse-param

YYParser上的构造函数: this(lex_param ..., parse_param, ...)

使用嵌入的 '' 构建一个新的解析器对象。没有 参数,除非使用 s 和/或 s 和/或 s。%code lexer%param%parse-param%lex-param

YYParser上的构造函数: this(Lexer lexerparse_param, ...)

使用指定的扫描程序生成新的分析器对象。没有 其他参数,除非 S 和/或 S 是 使用。%param%parse-param

YYParser 上的方法:boolean parse()

运行句法分析,否则返回成功。truefalse

YYParser 上的方法:boolean getErrorVerbose()
YYParser上的方法:void setErrorVerbose(boolean verbose

获取或设置用于生成详细错误消息的选项。这些只是 可用于 '', 这也会打开详细的错误消息。%define parse.error detailed

YYParser上的方法:void yyerror(string msg
YYParser上的方法:void yyerror(Location locstring msg

使用扫描仪的方法打印错误消息 实例正在使用中。和 参数为 仅当位置跟踪处于活动状态时才可用。yyerrorLocationPosition

YYParser 上的方法:boolean recovering()

在句法分析期间,如果恢复,则返回 来自语法错误。 请参阅错误恢复true

YYParser 上的方法:文件 getDebugStream()
YYParser上的方法:void setDebugStream(File o

获取或设置用于跟踪分析的流。它默认为 。stderr

YYParser 上的方法:int getDebugLevel()
YYParser上的方法:void setDebugLevel(int l

获取或设置跟踪级别。目前它的值为 0、无迹、 或非零,完全跟踪。

YYParser常量:string bisonVersion
YYParser的常量:string bisonSkeleton

确定用于生成此解析器的 Bison 版本和框架。

D 中的国际化与 C 中的国际化非常相似。The D 解析器用于翻译 Bison 消息。dgettext

要启用国际化,请使用 '' 和 import 和 from C 进行编译:-version ENABLE_NLS -version YYENABLE_NLSbindtextdomaintextdomain

extern(C) char* bindtextdomain(const char* domainname, const char* dirname);
extern(C) char* textdomain(const char* domainname);

main 函数应加载翻译目录,类似于示例:c/bistromathic

int main()
{
  import core.stdc.locale;

  // Set up internationalization.
  setlocale(LC_ALL, "");
  // Use Bison's standard translation catalog for error messages
  // (the generated messages).
  bindtextdomain("bison-runtime", BISON_LOCALEDIR);
  // For the translation catalog of your own project, use the
  // name of your project.
  bindtextdomain("bison", LOCALEDIR);
  textdomain("bison");

  // usual main content
  ...
}

对于用户消息翻译,用户必须实现“”函数。建议使用:string _(const char* msg)gettext

%code imports {
  static if (!is(typeof(_)))
  {
    version(ENABLE_NLS)
    {
      extern(C) char* gettext(const char*);
      string _(const char* s)
      {
        return to!string(gettext(s));
      }
    }
  }
  static if (!is(typeof(_)))
  {
    pragma(inline, true)
    string _(string msg) { return msg; }
  }
}

下一篇: , 上一篇: , 上一篇: D解析器[内容][索引]]

10.2.5 D 解析器上下文接口

解析器上下文提供用于在以下情况下生成错误报告的信息: 调用 ''。%define parse.error custom

YYParser的类型:SymbolKind

包含所有语法符号、标记和 非终端。它的枚举器是从符号名称中伪造出来的。用 '' 获取符号名称。void toString(W)(W sink)

YYParser.Context 上的方法:YYParser.SymbolKind getToken()

那种前瞻。返回时没有前瞻。null

YYParser.Context 上的方法:YYParser.Location getLocation()

前瞻的位置。

YYParser.Context 上的方法: int getExpectedTokens(YYParser.SymbolKind[] argvint argc

填充预期的标记,其中从不包含 或 。argvSymbolKind.YYERRORSymbolKind.YYUNDEF

永远不要把更多的元素放进去,在成功上 返回存储在 中的令牌数。如果还有更多 预期的令牌比 ,填满并返回 0. 如果没有预期的令牌,则也返回 0,但设置为 。argcargvargvargcargvargcargv[0]null

如果为 null,则返回存储所有可能文件所需的大小 值,它总是小于 。argvYYNTOKENS


下一篇: , 上一篇: , 上一篇: D解析器[内容][索引]

10.2.6 D扫描仪界面

有两种可能的方法可以连接 Bison 生成的 D 解析器 使用扫描仪:扫描仪可以由 或 定义 在其他地方定义。无论哪种情况,扫描程序都必须实现解析器类的内部接口。此接口还 包含所有用户定义的令牌名称和预定义令牌的常量。%code lexerLexerYYEOF

在第一种情况下,扫描仪类的主体被放置在块中。如果要从 Parser 构造函数添加到 scanner 构造函数中,用 ;它们在 s 之前传递给 构造 函数。%code lexer%lex-param%parse-param

在第二种情况下,扫描仪必须实现接口, 在解析器类中定义(例如,)。 然后,解析器对象的构造函数将接受一个对象 实现接口; 不在此使用 箱。LexerYYParser.Lexer%lex-param

在这两种情况下,扫描程序都必须实现以下方法。

Lexer 上的方法: void yyerror(Location locstring msg

此方法由用户定义,用于发出错误消息。第一个 如果位置跟踪未处于活动状态,则省略参数。

Lexer 上的方法:符号 yylex()

返回下一个令牌。返回值的类型为 ,其 将种类、语义值和位置结合在一起。Symbol

Lexer 上的方法:void reportSyntaxError(YYParser.Context ctx

如果调用 ''(请参阅 Bison 声明部分),则解析器不再将语法错误消息传递给 ,而是通过调用函数将该任务委托给用户。%define parse.error customyyerrorreportSyntaxError

是否使用取决于用户。yyerror

下面是一个报告函数的示例(请参阅 D 解析器上下文接口)。

public void reportSyntaxError(YYParser.Context ctx)
{
  stderr.write(ctx.getLocation(), ": syntax error");
  // Report the expected tokens.
  {
    immutable int TOKENMAX = 5;
    YYParser.SymbolKind[] arg = new YYParser.SymbolKind[TOKENMAX];
    int n = ctx.getExpectedTokens(arg, TOKENMAX);
    if (n < TOKENMAX)
      for (int i = 0; i < n; ++i)
        stderr.write((i == 0 ? ": expected " : " or "), arg[i]);
  }
  // Report the unexpected token which triggered the error.
  {
    YYParser.SymbolKind lookahead = ctx.getToken();
    stderr.writeln(" before ", lookahead);
  }
}

此实现不适合国际化,请参阅 更好的选择的例子。c/bistromathic


下一篇: , 上一篇: , 上一篇: D 解析器 [内容][索引]]

10.2.7 在 D 操作中使用的特殊功能

下面是一个 Bison 结构、变量和函数的表格,这些结构、变量和函数在 行动。

变量:$$

就像一个变量,其中包含 按当前规则进行分组。请参阅操作

变量:$n

就像一个变量,它包含当前规则的第 th 个组件的语义值。请参阅操作n

功能:yyerrok

立即恢复为后续语法生成错误消息 错误。这主要在错误规则中有用。 请参阅错误恢复


下一篇: , 上一篇: , 上一篇: D 解析器 [内容][索引]

10.2.8 D 推送解析器接口

通常,Bison 会为 D 生成一个拉取解析器。 以下 Bison 声明说您希望解析器是推送 解析器(参见 %define 摘要):

%define api.push-pull push

关于 D 拉取解析器接口的大多数讨论(参见 D 解析器接口)也适用于推送解析器接口。

生成推送解析器时,该方法是使用 以下签名:pushParse

YYParser 上的方法:int pushParse Symbol sym

与拉取解析器的主要区别在于解析器 重复调用方法以解析每个令牌。这 如果 '' 或 使用 '' 声明(请参阅 %define 摘要)。pushParse%define api.push-pull push%define api.push-pull both

该方法返回的值为下列值之一:、 或 。如果需要更多输入才能完成,则可能会返回此新值 解析输入。pushParseACCEPTABORTPUSH_MOREPUSH_MORE

如果定义为 ,则生成的解析器 类也将实现该方法。此方法的主体是 重复调用扫描程序,然后传递获取的值的循环 从扫描仪到方法。api.push-pullbothparsepushParse


上一篇: , 上一篇: D 解析器 [内容][索引]

10.2.9 D 完整符号

要生成 的返回值,请调用 与报告的令牌类型同名,并添加 值和位置(如有必要)。这些方法会生成编译时错误 如果参数不一致。令牌构造函数同时使用 和 ''。yylexSymbol%union%define api.value.type union

参数的顺序与构造函数的顺序相同。令牌类型示例,该类型具有值并激活了位置跟踪:SymbolNUMival

Symbol.NUM(ival, location);

10.3 Java 解析器


下一篇: , 上一篇: Java 解析器 [内容][索引]

10.3.1 Java Bison 接口

Java 解析器框架是使用指令或 / 选项选择的。%language "Java"-L java--language=java

生成 Java 解析器时,“' 将创建一个 单个 Java 源文件,其中包含 解析器实现。使用不带后缀的语法文件是 目前坏了。解析器实现文件的基名可以是 由指令或 / 选项更改。整个解析器实现 可以通过指令或 / 选项更改文件名。分析器实现文件 包含分析器的单个类。bison basename.ybasename.java.y%file-prefix-b--file-prefix%output-o--output

您可以使用 Javadoc 为生成的解析器创建文档。

与 C 解析器相反,Java 解析器不使用全局变量;国家 的解析器始终是解析器类实例的本地变量。 因此,所有 Java 解析器都是“纯”的,在 Java 中使用时该指令不执行任何操作。%define api.pure

Java 目前不支持 GLR 解析器。不要使用该指令。glr-parser

不能为 Java 解析器生成头文件。不要使用指令或 // 选项。%header-d-H--header

目前,始终编译了对跟踪的支持。因此, '' 和 '' 指令以及 / 和 / 选项 没有效果。这将来可能会改变,以消除 生成的解析器,因此如果 需要。此外,将来该指令可能会启用 用于访问令牌名称和代码的公共接口。%define parse.trace%token-table-t--debug-k--token-table%define parse.trace%token-table

从 Java 编译器收到“代码太大”错误意味着代码命中 Java 类文件的每个方法限制为 64KB 字节码。尝试 减少操作和静态初始值设定项中的代码量;否则 报告错误,以便改进解析器框架。


Next: , Previous: , Up: Java 解析器 [Contents][索引]]

10.3.2 Java 语义值

Java 解析器中没有指令。取而代之的是,语义 值的类型(类名)应在 OR 指令中指定:%union%nterm%token

%nterm <Expression> expr assignment_expr term factor
%nterm <Integer> number

默认情况下,语义堆栈声明为具有成员, 这意味着您指定的类类型可以是任何类。 为了提高解析器的类型安全性,可以声明 common 所有语义值的超类,使用 '' 命令。例如,在以下声明之后:Object%define api.value.type

%define api.value.type {ASTNode}

any ,或指定语义类型 它不是 的子类,将导致编译时错误。%token%nterm%typeASTNode

指令中使用的类型可以使用包名称进行限定。 Java V1.5 或更高版本接受基元数据类型。注意 在这种情况下,将使用 Java 1.5 的自动装箱功能。 不能使用泛型类型;这是由于 Bison 的实现,并且可能会在将来的版本中发生变化。

Java 解析器不支持 ,因为该语言 采用垃圾回收。解析器将尝试保存引用 根据需要尽可能短的时间进行语义值。%destructor

Java 解析器不支持 ,因为它可用于打印语义值。但是,这可能会改变 (以向后兼容的方式)在 Bison 的未来版本中。%printertoString()


Next: , 上一篇: , 上一篇: Java 解析器 [Contents][Index]

10.3.3 Java 位置值

使用该指令时,Java 解析器支持 位置跟踪,请参阅跟踪位置。辅助用户定义 class 定义一个位置,一个文件中的单个点;野牛本身 定义一个表示位置的类,该范围由一对 位置(可能跨越多个文件)。location 类是内部 解析器的类;默认情况下,该名称是,也可能是 使用 重命名。%locationsLocation%define api.location.type {class-name}

位置类将位置视为完全不透明的值。 默认情况下,类名为 ,但可以更改 跟。此类必须 由用户提供。Position%define api.position.type {class-name}

位置的实例变量:位置开始
位置的实例变量:位置结束

范围的第一个(包括)位置,以及第一个超越的位置。

Constructor on Location: Location 位置位置

创建一个表示位于给定点的空范围。Location

位置构造函数: 位置位置开始位置结束

从范围的终结点创建一个。Location

位置方法:String toString()

打印由位置表示的范围。为此要起作用 正确地,position 类应适当地重写 and 方法。equalstoString


10.3.4 Java 解析器接口

生成的分析器类的名称默认为 。可以使用“”更改前缀。 或者,使用 '' 给出一个 类的自定义名称。下面详细介绍了此类的接口。YYParserYY%define api.prefix%define api.parser.class {name}

默认情况下,分析器类具有包可见性。宣言 '' 将更改为公开可见性。记得 根据 Java 语言规范,在这种情况下,文件的名称应与类的名称匹配。 同样,可以使用 和 与声明一起添加 Parser 类的其他修饰符。可以使用单个 '' 指令来添加 对 Parser 类的任意数量的批注。%define api.parser.public.javaapi.parser.abstractapi.parser.finalapi.parser.strictfp%define%define api.parser.annotations {annotations}

解析器类的 Java 包名称可以使用 '' 指令。超类和实现的 可以使用 和 '' 指令指定解析器类的接口。%define package%define api.parser.extends%define api.parser.implements

解析器类定义一个内部类 ,用于 用于位置跟踪(请参阅 Java 位置值)和内部 接口(请参阅 Java 扫描程序接口)。以外 这些内部类/接口,以及接口中描述的成员 下面,所有其他成员和字段前面都带有 OR 前缀,以避免与用户代码发生冲突。LocationLexeryyYY

可以使用该指令扩展解析器类。该指令的每次出现都会向解析器类添加一个字段,并向其构造函数添加一个参数, 它会自动初始化它们。%parse-paramprotected final

YYParser上的构造函数: YYParser lex_param..., parse_param, ...)

使用嵌入式 . 构建一个新的解析器对象。有 没有参数,除非使用 s 和/或 s 和/或 s。%code lexer%param%parse-param%lex-param

用于添加到构造函数开头的代码 身体。这对于初始化超类特别有用。用 '' 指定任何未捕获的异常。%code init%define init_throws

YYParser上的构造函数: YYParser Lexer lexerparse_param, ...)

使用指定的扫描程序生成新的分析器对象。没有 其他参数,除非 S 和/或 S 是 使用。%param%parse-param

如果扫描程序由 定义,则此构造函数为 声明并使用扫描程序自动调用 使用正确的 S 和/或 S 创建。%code lexerprotected%param%lex-param

用于添加到构造函数开头的代码 身体。这对于初始化超类特别有用。用 '' 指定任何未捕获的异常。%code init%define init_throws

YYParser上的方法:boolean parse ()

运行句法分析,否则返回成功。truefalse

YYParser 上的方法:boolean getErrorVerbose ()
YYParser 上的方法:void setErrorVerbose (boolean verbose

获取或设置用于生成详细错误消息的选项。这些只是 可用于 ''(或 ''), 这也会打开详细的错误消息。%define parse.error detailedverbose

YYParser上的方法:void yyerror String msg
YYParser上的方法:void yyerror Position posString msg
YYParser上的方法:void yyerror Location locString msg

使用扫描仪的方法打印错误消息 实例正在使用中。和 参数为 仅当位置跟踪处于活动状态时才可用。yyerrorLocationPosition

YYParser 上的方法:布尔恢复 ()

在句法分析期间,如果恢复,则返回 来自语法错误。 请参阅错误恢复true

YYParser上的方法:java.io.PrintStream getDebugStream ()
YYParser 上的方法:void setDebugStream java.io.PrintStream o

获取或设置用于跟踪分析的流。它默认为 。System.err

YYParser 上的方法:int getDebugLevel ()
YYParser上的方法:void setDebugLevel int l

获取或设置跟踪级别。目前它的值为 0、无迹、 或非零,完全跟踪。

YYParser常量:String bisonVersion
YYParser的常量:String bisonSkeleton

确定用于生成此解析器的 Bison 版本和框架。

如果启用了令牌国际化(请参阅令牌国际化),则必须 为解析器提供以下函数:

YYParser的静态方法:String i18nstring s

返回用户语言的翻译。举个例子:s

%code {
  static ResourceBundle myResources
    = ResourceBundle.getBundle("domain-name");
  static final String i18n(String s) {
    return myResources.getString(s);
  }
}

下一篇: , 上一篇: , 上一篇: Java 解析器 [内容][索引]

10.3.5 Java 解析器上下文接口

解析器上下文提供用于在以下情况下生成错误报告的信息: 调用 ''。%define parse.error custom

YYParser的类型:SymbolKind

所有语法符号、标记和非终端的枚举。其 枚举器是从符号名称伪造的:

public enum SymbolKind
{
  S_YYEOF(0),          /* "end of file"  */
  S_YYERROR(1),        /* error  */
  S_YYUNDEF(2),        /* "invalid token"  */
  S_BANG(3),           /* "!"  */
  S_PLUS(4),           /* "+"  */
  S_MINUS(5),          /* "-"  */
  [...]
  S_NUM(13),           /* "number"  */
  S_NEG(14),           /* NEG  */
  S_YYACCEPT(15),      /* $accept  */
  S_input(16),         /* input  */
  S_line(17);          /* line  */
};
YYParser.SymbolKind 上的方法:字符串 getName ()

这个符号的名称,可能是翻译的。

YYParser.Context 上的方法:YYParser.SymbolKind getToken ()

那种前瞻。返回时没有前瞻。null

YYParser.Context 上的方法:YYParser.Location getLocation ()

前瞻的位置。

YYParser.Context 上的方法: int getExpectedTokens YYParser.SymbolKind[] argvint argc

填充预期的标记,其中从不包含 或 。argvSymbolKind.S_YYERRORSymbolKind.S_YYUNDEF

永远不要把更多的元素放进去,在成功上 返回存储在 中的令牌数。如果还有更多 预期的令牌比 ,填满并返回 0. 如果没有预期的令牌,则也返回 0,但设置为 。argcargvargvargcargvargcargv[0]null

如果为 null,则返回存储所有可能文件所需的大小 值,它总是小于 。argvYYNTOKENS


下一篇: , 上一篇: , 上一篇: Java 解析器 [内容][索引]

10.3.6 Java 扫描程序界面

有两种可能的方法可以连接 Bison 生成的 Java 解析器 使用扫描仪:扫描仪可以由 或 定义 在其他地方定义。无论哪种情况,扫描程序都必须实现解析器类的内部接口。此接口还 包含所有用户定义的令牌名称和预定义令牌的常量。%code lexerLexerYYEOF

在第一种情况下,扫描仪类的主体被放置在块中。如果要从 Parser 构造函数添加到 scanner 构造函数中,用 ;它们在 s 之前传递给 构造 函数。%code lexer%lex-param%parse-param

在第二种情况下,扫描仪必须实现接口, 在解析器类中定义(例如,)。 然后,解析器对象的构造函数将接受一个对象 实现接口; 不在此使用 箱。LexerYYParser.Lexer%lex-param

在这两种情况下,扫描程序都必须实现以下方法。

Lexer 上的方法: void yyerror Location locString msg

此方法由用户定义,用于发出错误消息。第一个 如果位置跟踪未处于活动状态,则省略参数。其类型可以是 使用 更改了 。%define api.location.type {class-name}

Lexer 上的方法: int yylex ()

返回下一个令牌。它的类型是返回值、语义值和 位置由界面中的 THEIR 方法保存和返回。不 仅推送解析器需要。

使用 '' 指定任何未捕获的异常。 默认值为 。%define lex_throwsjava.io.IOException

Lexer 上的方法:定位 getStartPos ()
Lexer 上的方法:定位 getEndPos ()

分别返回返回的最后一个令牌的第一个位置,以及超出它的第一个位置。不需要这些方法 除非位置跟踪和拉取解析处于活动状态。yylex

他们应该为每次调用返回新对象,以避免所有符号 共享相同的位置边界。

可以使用 更改返回类型。%define api.position.type {class-name}

Lexer 上的方法:对象 getLVal ()

返回 yylex 返回的最后一个标记的语义值。不需要 用于仅推送解析器。

可以使用 '' 更改返回类型。%define api.value.type {class-name}

Lexer 上的方法:void reportSyntaxError YYParser.Context ctx

如果调用 ''(请参阅 Bison 声明部分),则解析器不再将语法错误消息传递给 ,而是通过调用函数将该任务委托给用户。%define parse.error customyyerrorreportSyntaxError

是否使用取决于用户。yyerror

下面是一个报告函数的示例(请参阅 Java 解析器上下文接口)。

public void reportSyntaxError(YYParser.Context ctx) {
  System.err.print(ctx.getLocation() + ": syntax error");
  // Report the expected tokens.
  {
    final int TOKENMAX = 5;
    YYParser.SymbolKind[] arg = new YYParser.SymbolKind[TOKENMAX];
    int n = ctx.getExpectedTokens(arg, TOKENMAX);
    for (int i = 0; i < n; ++i)
      System.err.print((i == 0 ? ": expected " : " or ")
                       + arg[i].getName());
  }
  // Report the unexpected token which triggered the error.
  {
    YYParser.SymbolKind lookahead = ctx.getToken();
    if (lookahead != null)
      System.err.print(" before " + lookahead.getName());
  }
  System.err.println("");
}

此实现不适合国际化,请参阅示例以获取更好的替代方案。c/bistromathic


下一篇: , 上一篇: , 上一篇: Java 解析器 [Contents][Index]

10.3.7 Java Actions 中使用的特殊功能

以下特殊构造可以在 Java 操作中使用。 其他类似的 C 操作功能目前不适用于 Java。

使用 '' 指定解析器中任何未捕获的异常 操作,以及 指定的初始操作。%define throws%initial-action

变量:$n

当前规则的第 th 个组件的语义值。 这可能不会分配给。 请参阅 Java 语义值n

变量:$<typealt>n

喜欢,但指定替代类型。 请参阅 Java 语义值$ntypealt

变量:$$

当前规则创建的分组的语义值。作为 值,这是基类型(或由 '') 如 not 强制转换为声明的子类型,因为 不允许在 Java 赋值的左侧进行强制转换。 如果需要正确的子类型,请使用显式 Java 强制转换。 请参阅 Java 语义值Object%define api.value.type

变量:$<typealt>$

与 Java 始终允许分配给基类型相同。 也许我们应该使用它来表示值和设置值,但目前没有简单的方法来区分 这些结构。 请参阅 Java 语义值$$$<>$$$

变量:@n

当前规则的第 th 个组件的位置信息。 这可能不会分配给。 请参阅Java 位置值n

变量:@$

当前规则创建的分组的位置信息。 请参阅Java 位置值

语句: return YYABORT ;

立即从解析器返回,指示失败。 请参阅 Java 解析器接口

语句: return YYACCEPT ;

立即从解析器返回,表示成功。 请参阅 Java 解析器接口

语句: return YYERROR ;

启动错误恢复(不打印错误消息)。 请参阅错误恢复

功能:布尔恢复 ()

返回是否正在执行错误恢复。在此状态下,解析器 读取令牌,直到它达到已知状态,然后重新启动正常 操作。 请参阅错误恢复

函数:void yyerror String msg
函数: void yyerror 位置 loc字符串 msg
函数: void yyerror 位置 loc字符串消息

使用扫描仪的方法打印错误消息 实例正在使用中。和 参数为 仅当位置跟踪处于活动状态时才可用。yyerrorLocationPosition


10.3.8 Java 推送解析器接口

通常,Bison 会为 Java 生成一个拉取解析器。 以下 Bison 声明说您希望解析器是推送 解析器(参见 %define 摘要):

%define api.push-pull push

关于 Java 拉取解析器接口的大多数讨论(参见 Java 解析器接口)也适用于推送解析器接口。

生成推送解析器时,该方法是使用 以下签名(取决于是否启用了位置)。push_parse

YYParser上的方法:void push_parse int tokenObject yylval
YYParser上的方法:void push_parse int tokenObject yylvalLocation yyloc
YYParser上的方法:void push_parse int tokenObject yylvalPosition yypos

与拉取解析器的主要区别在于解析器 重复调用方法以解析每个令牌。这 如果 '' 或 使用 '' 声明(请参阅 %define 摘要)。和 参数可用 仅当位置跟踪处于活动状态时。push_parse%define api.push-pull push%define api.push-pull bothLocationPosition

该方法返回的值为下列值之一: 0 (成功)、1 (中止)、2 (内存耗尽) 或 .这 如果需要更多输入,则可以返回新值 完成解析语法。push_parseYYPUSH_MOREYYPUSH_MORE

如果定义为 ,则生成的解析器 类也将实现该方法。此方法的主体是 重复调用扫描程序,然后传递获取的值的循环 从扫描仪到方法。api.push-pullbothparsepush_parse

还有一个额外的并发症。从技术上讲,推送解析器不会 需要了解扫描程序(即实现接口的对象),但它确实需要访问该方法。目前,该方法定义在 接口。因此,实现这一点 为了提供 的实现,仍然需要接口。目前的方法(可能会发生变化)是要求 要为实现接口的对象指定的构造函数。这个对象只需要实现方法;其他方法可以存根,因为它们会 永远不要被调用。最简单的方法是添加一个微不足道的扫描仪 使用所需的任何实现实现到语法文件。下面的代码示例演示了一种简单的方法 完成此操作。YYParser.LexeryyerroryyerrorYYParser.LexeryyerrorYYParserYYParser.Lexeryyerroryyerror

%code lexer
{
  public Object getLVal () {return null;}
  public int yylex () {return 0;}
  public void yyerror (String s) {System.err.println(s);}
}

下一篇: , 上一篇: , 上一篇: Java 解析器 [Contents][索引]]

10.3.9 C/C++ 和 Java 语法的区别

Java 语言的不同结构迫使存在一些差异 在 C/C++ 语法和为 Java 解析器设计的语法之间。这 部分总结了这些差异。

  • Java 没有预处理器,所以很明显,、、符号(参见 Bison Symbols)不能是 宏。相反,它们应该在它们出现时之前 在行动中。这些符号的实际定义对野牛来说是不透明的 语法,将来可能会改变。唯一有意义的操作 你能做的,就是归还它们。请参阅在 Java 操作中使用的特殊功能。YYERRORYYACCEPTYYABORTreturn

    请注意,在这三个符号中,只有 和 将导致从方法8 返回。YYACCEPTYYABORTyyparse

  • Java 缺少联合,因此没有效果。取而代之的是语义 值具有通用基类型:或由 ‘’.尖括号 、 和 指定子类型而不是 一个工会。的类型,即使有尖括号,也是基数 类型,因为不允许在赋值的左侧进行 Java 强制转换。 此外,不允许在 作业的左侧。请参阅 Java 语义值用于 Java 操作的特殊功能%unionObject%define api.value.type%tokentype$n$$$$$n@n
  • 序言声明的含义与 C/C++ 代码中的含义不同。
    %code imports

    块放在 Java 源代码的开头。他们可能 包括版权声明。对于声明,请使用 '' 代替。package%define api.package

    不合格的 %code

    块放置在 Parser 类中。

    %code lexer

    块(如果指定)应包括 扫描器。如果没有这样的块,扫描仪可以是任何类 实现适当的接口(请参阅 Java 扫描程序接口)。

    Java 解析器不支持其他块。 特别是,不应使用块 并可能在 Bison 的未来版本中出现错误。%code%{ … %}

    结语与 C/C++ 代码中的含义相同,它可以 用于定义解析器在解析器类之外使用的其他类。


10.3.10 Java 声明摘要

此摘要仅包括特定于 Java 的声明或具有特殊 在 Java 解析器中使用时的含义。

指令:%language “Java”

为解析器生成一个 Java 类。

指令:%lex-param {type name}

由 only 定义的词法分析器类的参数,作为参数添加到词法分析器构造函数和解析器中 创建词法分析器的构造函数。默认值为 none。 请参阅 Java 扫描程序接口%code lexer

指令:%parse-param {type name}

作为参数添加到构造函数的解析器类的参数 并作为由构造函数初始化的字段。默认值为 none。 请参阅 Java 解析器接口

指令:%token <type> token ...

声明令牌。请注意,尖括号括起来的是 Java 类型请参阅 Java 语义值

指令: %nterm <type> nonterminal ...

声明非终端的类型。请注意,尖括号括起来 Java 类型请参阅 Java 语义值

指令: %code { code ... }

追加到分析器类内部的代码。 请参阅 C/C++ 和 Java 语法之间的差异

指令: %code imports { code ... }

在声明之后插入的代码。 请参阅 C/C++ 和 Java 语法之间的差异package

指令: %code init { code ... }

在分析器构造函数主体的开头插入的代码。 请参阅 Java 解析器接口

指令: %code lexer { code ... }

添加到分析器类内词法分析器类的主体的代码。 请参阅 Java 扫描程序接口

指令: %% code ...

代码(在第二个之后)追加到文件末尾,在解析器类之外。 请参阅 C/C++ 和 Java 语法之间的差异%%

指令: %{ code ... %}

不支持。请改用。 请参阅 C/C++ 和 Java 语法之间的差异%code imports

指令:%define api.prefix {prefix}

如果 '' 未使用。默认值为 。 请参阅 Java Bison 接口prefixParser%define api.parser.classYY

指令:%define api.parser.abstract

是否声明解析器类。默认值为 false。 请参阅 Java Bison 接口abstract

指令:%define api.parser.annotations {annotations}

解析器类的 Java 注释。默认值为 none。 请参阅 Java Bison 接口

指令:%define api.parser.class {name}

分析器类的名称。默认值为 或 。请参阅 Java Bison 接口YYParserapi.prefixParser

指令:%define api.parser.extends {superclass}

解析器类的超类。默认值为 none。 请参阅 Java Bison 接口

指令:%define api.parser.final

是否声明解析器类。默认值为 false。 请参阅 Java Bison 接口final

指令:%define api.parser.implements {interfaces}

解析器类的实现接口,一个逗号分隔的列表。 默认值为 none。 请参阅 Java Bison 接口

指令:%define api.parser.public

是否声明解析器类。默认值为 false。 请参阅 Java Bison 接口public

指令:%define api.parser.strictfp

是否声明解析器类。默认值为 false。 请参阅 Java Bison 接口strictfp

指令:%define init_throws {exceptions}

从解析器类引发的异常 构造 函数。默认值为 none。 请参阅 Java 解析器接口%code init

指令:%define lex_throws {exceptions}

词法分析器方法引发的异常,一个 逗号分隔的列表。默认值为 。 请参阅 Java 扫描程序接口yylexjava.io.IOException

指令:%define api.location.type {class}

用于位置的类的名称(介于 2 之间的范围 位置)。此类作为分析器的内部类生成 类 由 .默认值为 。 原名 . 请参阅Java 位置值bisonLocationlocation_type

指令:%define api.package {package}

要放入分析器类的包。默认值为 none。 请参阅 Java Bison 接口。 从 Bison 3.7 中重命名。package

指令:%define api.position.type {class}

用于职位的类的名称。此类必须由 用户。默认值为 。 原名 . 请参阅Java 位置值Positionposition_type

指令:%define api.value.type {class}

语义值的基本类型。默认值为 。 请参阅 Java 语义值Object

指令: %define throws {exceptions}

用户提供的分析器操作引发的异常,以及逗号分隔的列表。默认值为 none。 请参阅 Java 解析器接口%initial-action


11 大有蹄类动物简史


11.1 祖先的Yacc

Bison 起源于一个名为 Yacc — Yet Another 的程序的类似工作 编译器编译器。9 Yacc 是在贝尔实验室编写的,是早期的一部分 Unix的开发;它的第一个用途是开发原始的 可移植 C 编译器,pcc。同一个人,史蒂文·约翰逊(Steven C. Johnson),写了Yacc和 原始 PCC。

根据作者 10 的说法, Yacc 于 1971 年首次发明,并达到了一种可识别的类似于 1973 年的 C 版本。Johnson 出版了 A Portable Compiler: Theory 和实践(见Johnson 1978)。

Yacc 本身最初不是用 C 语言编写的,而是用它的前身语言编写的, B.这在很大程度上解释了它的奇怪界面,它暴露了大量的 的全局变量,而不是将它们捆绑到 C 结构中。所有其他 类似 Yacc 的程序是从 Yacc 的 C 端口衍生而来的。

Yacc,通过其在 pcc 中的部署和作为 生成其他解析器,帮助推动了 Unix 的早期传播。雅克 然而,在 1990 年左右之后,当 Workalikes 随着许可证限制的减少和更多功能的可用。

当Caldera发布源代码时,原始Yacc已普遍可用 2002 年旧版本的 Unix 高达 V7 和 32V。到那时,它已经 在实际使用中早已被 Bison 取代,甚至在 Yacc 的原生 Unix 上也是如此 变种。


11.2 雅克

原始 Yacc 的缺陷之一是它无法生产 可重入解析器。这首先通过一组插入式补救措施 名为“yacchack”的修改,由Eric S. Raymond在USENET上发布 1983年左右。当 zoo 和 Berkeley Yacc 时,这段代码很快就被遗忘了 几年后可用。


下一篇: , 上一篇: , 上一篇: 大有蹄类动物简史 [目录][索引]

11.3 伯克利雅克

Berkeley Yacc 由 Robert Corbett 于 1985 年创立(参见 Corbett 1984)。它最初被命名为“动物园”,但到 1989 年 10 月它变成了 被称为伯克利 Yacc 或 byacc。

伯克利 Yacc 与祖先的 Yacc 相比有三个优势:它产生了 更快的解析器,它可以生成可重入的解析器,并且源代码是 发布到公共领域,而不是在AT&T的专有下 许可证。更好的性能来自于实现以下技术 DeRemer 和 Penello 关于 LALR 解析的开创性论文(参见 DeRemer 1982)。

由于其公共领域许可,byacc的使用迅速传播。然而,一旦 Bison 变得可用,byacc 本身已经不再普遍使用。


下一篇: , 上一篇: , 上一篇: 大有蹄类动物简史 [内容][索引]

11.4 野牛

Robert Corbett 实际上在 1985 年编写了两个(密切相关的)LALR 解析器, 两者都使用 DeRemer/Penello 技术。一个是“动物园”,另一个是 “拜森”。1987年,理查德·斯托曼(Richard Stallman)开始研究拜森(Byson);名称已更改 到 Bison,界面变得与 Yacc 兼容。

Yacc 和 Byson/Bison 之间的主要可见区别 Byson 的第一个版本是 Byson 支持该构造 (允许访问起始和结束行号和字符号 与当前规则中的任何符号相关联)。@n

还有命令''说不要提及 如果存在 shift/reduce 冲突且没有 reduce/reduce 冲突,则发生冲突 冲突。在较新版本的 Bison 中,其 reduce/reduce 冲突的变体可以应用于 个人规则。%expect nn%expect%expect-rr

更高版本的 Bison 添加了更多新功能。

Bison 错误报告已通过各种方式得到改进。特别是。祖先的 Yacc 和 Byson 在错误消息中没有插入符号。

与 Yacc 相比,Bison 使用更快但空间效率更低的编码 解析表(参见 Corbett 1984),以及更现代的技术 生成前瞻集(参见 DeRemer 1982)。这种方法是 从那时起,标准一。

(也有人合理地声称算法的差异 主要来自约翰逊不得不犯下的可怕的 kludges 原来的 Yacc 适合 PDP-11。

命名引用、语义谓词、、%析构函数、转储到 DOT、、、 和转储到 XSLT、LAC 和 IELR(1) 一代是野牛的新世代。%locations%glr-parser%printer%parse-param%lex-param

Bison 还具有许多支持 C++ 的功能,这些功能在 祖先 Yacc 或 Byson。

Bison 淘汰了所有以前的 Yacc 变体和 workalikes 生成 C 1995.


上一页: , 上篇: 大有蹄类动物简史 [目录][索引]

11.5 其他有蹄类动物

Yacc 概念经常被移植到其他语言。一些 早期的港口与承载它们的语言一起灭绝了;别人 已被 Bison 随附的解析器骨架所取代。

但是,独立的实现仍然存在。最有名的之一 仍在使用的是 David Beazley 的“PLY”(Python Lex-Yacc) 蟒。另一个是 goyacc,支持 Go 语言。一个“ocamlyacc” 作为 Ocaml 编译器套件的一部分提供。


下一篇: , 上一篇: , 上一篇: 野牛 [目录][索引]

12 Bison 版本兼容性:最佳实践

Bison 提供了一种 Yacc 兼容模式,在该模式下,它努力符合 POSIX 标准。按照 POSIX 标准编写的语法文件,以及 不要利用野牛的任何特殊能力,应该 无需修改即可与许多版本的 Bison 一起使用。

Bison 的所有其他功能都是 Bison 特有的,并且正在发生变化。野牛 积极维护并不断发展。它应该是否定的 令人惊讶的是,旧版本的 Bison 不会接受 Bison 源代码 使用较旧的 Bison 中根本不存在的较新功能。 令人遗憾的是,尽管为保持兼容性做出了合理的努力, 也可能发生相反的情况:可能会发生使用 旧版本的 Bison 不会在没有 修改。

因为 Bison 是一个代码生成工具,所以可以保留其输出 并将其分发给程序的用户。然后用户不是 需要完全安装 Bison,只是实现 编程语言,如C,这是处理生成的 输出。

Bison 的输出旨在具有最大的可移植性。 所以,也就是说,而 Bison 语法源代码可能具有依赖性 在特定版本的 Bison 上,从任何版本的 Bison 生成的解析器 应该与大量的 C 实现一起工作,或者其他什么 语言是适用的。

使用 Bison 的推荐最佳实践(在软件上下文中 以源代码形式分发)是将生成的解析器发送到 下游用户。只有那些积极从事开发的下游用户 需要对语法文件进行更改的程序需要有 Bison 完全安装,这些用户可以安装特定版本的 Bison 这是必需的。

遵循此推荐做法还可以使用更新的 Bison比用户通过操作系统发行版提供的内容, 从而利用 Bison 允许的最新技术。

Bison 的一些特征已经或正在被采用到其他类似 Yacc 的 Yacc 中 程序。因此,编写语法代码似乎是个好主意 它针对多个实现,类似于 C 程序的方式 通常针对多个编译器和语言版本编写。以外 POSIX 描述的 Yacc 子集,Bison 语言并不严格 标准化。当 Bison 功能被另一个解析器生成器采用时,它 最初可能与它所基于的 Bison 版本兼容, 但兼容性可能会在未来下降。努力使 他们的 Bison 代码同时与其他解析器生成器兼容是 尽管如此,我们仍然鼓励使用所有生成器的特定版本,并且仍然 请遵循传送生成输出的建议做法。例如 一个项目可以在内部保持与多个生成器的兼容性, 并选择要交付给用户的特定输出。否则 该项目可以交付所有输出,为用户安排一种方式 指定用于构建程序的程序。


下一篇: , 上一篇: , 上一篇: 野牛 [内容][索引]

13 常见问题解答

关于野牛的几个问题偶尔会出现。这里有一些 已解决。


下一篇: ,向上:常见问题 [目录][索引]

13.1 内存耗尽

我的解析器返回错误,并显示“” 消息。我能做些什么?memory exhausted

这个问题已经在其他地方解决了,请参阅递归规则


下一篇: , 上一篇: , 上一篇: 常见问题 [内容][索引]]

13.2 如何重置解析器

以下现象有几种症状,导致 以下典型问题:

我调用了几次,在正确的输入下它就可以工作了 适当地;但是,当发现解析错误时,所有其他调用都会失败 太。如何重置 的错误标志?yyparseyyparse

我的解析器包括对类似“”功能的支持,其中 我逃避的案例.尽管我做到了,但这失败了 指定 ''。#includeyyparseyyparse%define api.pure full

这些问题通常不是来自野牛本身,而是来自 Lex 生成的扫描仪。因为这些扫描仪使用较大的缓冲区 速度,他们可能不会注意到输入文件的变化。作为 演示,请考虑以下源文件:first-line.l

%{
#include <stdio.h>
#include <stdlib.h>
%}
%%
.*\n    ECHO; return 1;
%%
int
yyparse (char const *file)
{
  yyin = fopen (file, "r");
  if (!yyin)
    {
      perror ("fopen");
      exit (EXIT_FAILURE);
    }
  /* One token only. */
  yylex ();
  if (fclose (yyin) != 0)
    {
      perror ("fclose");
      exit (EXIT_FAILURE);
    }
  return 0;
}
int
main (void)
{
  yyparse ("input");
  yyparse ("input");
  return 0;
}

如果文件包含input

input:1: Hello,
input:2: World!

然后,您得到的不是两次第一行,而是:

$ flex -ofirst-line.c first-line.l
$ gcc  -ofirst-line   first-line.c -ll
$ ./first-line
input:1: Hello,
input:2: World!

因此,每当您更改时,都必须告诉 Lex 生成的扫描程序,用于丢弃其当前缓冲区并切换到 新的。这取决于您对 Lex 的实施;查看其 文档了解更多信息。对于 Flex,调用 '' 在每次更改为 .如果你的 Flex 生成的扫描仪需要从多个输入流读取到 处理包含文件等功能,您可以考虑使用 Flex 像 '' 这样的函数,用于操作多个 输入缓冲区。yyinYY_FLUSH_BUFFERyyinyy_switch_to_buffer

如果 Flex 生成的扫描仪使用启动条件(请参阅《Flex 手册》中的启动条件),则可以 还想重置扫描仪的状态,即回到初始 启动条件,通过调用 ''.BEGIN (0)


下一篇: , 上一篇: , 上一篇: 常见问题 [内容][索引]

13.3 字符串被销毁

我的解析器似乎破坏了旧字符串,或者它可能失去了对 他们。它不是报告“”,而是报告 '',甚至''。"foo", "bar""bar", "bar""foo\nbar", "bar"

此错误可能是发送到的最常见的“错误报告”。 野牛列出,但只关心对角色的误解 扫描仪。请考虑以下 Lex 代码:

%{
#include <stdio.h>
char *yylval = NULL;
%}
%%
.*    yylval = yytext; return 1;
\n    continue;
%%
int
main ()
{
  /* Similar to using $1, $2 in a Bison action. */
  char *fst = (yylex (), yylval);
  char *snd = (yylex (), yylval);
  printf ("\"%s\", \"%s\"\n", fst, snd);
  return 0;
}

如果编译并运行此代码,则会获得:

$ flex -osplit-lines.c split-lines.l
$ gcc  -osplit-lines   split-lines.c -ll
$ printf 'one\ntwo\n' | ./split-lines
"one
two", "two"

这是因为是为在操作中读取而提供的缓冲区,但如果你想保留它,你必须复制它 (例如,使用 )。请注意,输出可能取决于如何 您的 Lex 句柄实现 。例如,当 给定 Lex 兼容性选项(触发 option '') Flex 会生成不同的行为:yytextstrdupyytext-l%array

$ flex -l -osplit-lines.c split-lines.l
$ gcc     -osplit-lines   split-lines.c -ll
$ printf 'one\ntwo\n' | ./split-lines
"two", "two"

下一篇: , 上一篇: , 上一篇: 常见问题 [内容][索引]

13.4 实现 gotos/loops

我的简单计算器支持变量、赋值和函数, 但是我怎样才能实现 gotos 或循环呢?

虽然非常具有教育意义,但文档中包含的示例模糊不清 解析器之间的区别 - 其工作是恢复 文本的结构,并将其传输到后续模块 程序 - 以及此的处理(例如执行) 结构。这适用于所谓的直线程序, 即,恰恰是那些具有简单执行模型的: 一个接一个地执行简单的指令。

如果你想要一个更丰富的模型,你可能需要使用解析器 构造一棵树来表示它所具有的结构 恢复;这棵树通常被称为抽象语法树, 或简称 AST。然后,穿过这棵树, 以各种方式遍历它,将使诸如 执行或其翻译,这将导致口译员或 编译器。

这个主题远远超出了本手册的范围,读者是 邀请查阅专门的文献。


下一篇: , Previous: , up: 常见问题 [目录][索引]

13.5 多个开始符号

我有几个密切相关的语法,我想分享他们的 实现。事实上,我可以使用单一语法,但可以使用多个语法 入口点。

Bison 不支持多个开始符号,但有一个非常简单的 意味着模拟它们。如果 和 是两个伪 start-symbols,然后引入两个新标记,比如 和 ,并将它们用作实际 start-symbol 的开关:foobarSTART_FOOSTART_BAR

%token START_FOO START_BAR;
%start start;
start:
  START_FOO foo
| START_BAR bar;

这些令牌可防止引入新的冲突。至于 解析器说,这就是所需要的。

现在困难的部分是确保扫描程序将发送这些令牌 第一。如果您的扫描仪是手写的,那应该很简单。如果 您的扫描仪是由 Lex 生成的,他们有简单的方法可以做到这一点: 回想一下,第一个之后的“”之间的任何内容都是 在生成的函数顶部逐字复制。做 确保扫描仪中有一个变量可用(例如,一个 全局变量或使用等),并使用以下命令:%{ ... %}%%yylexstart_token%lex-param

  /* Prologue. */
%%
%{
  if (start_token)
    {
      int t = start_token;
      start_token = 0;
      return t;
    }
%}
  /* The rules. */

Next: 性, Previous: , Up: 常见问题 [Contents][Index]

13.6 安全?遵守?

野牛安全吗?是否符合POSIX?

如果您正在寻找保证或认证,我们不提供。 但是,Bison 旨在成为一个符合 Yacc 的 POSIX 规范。如果您遇到问题,请给我们发送 错误报告。


下一篇: , 上一篇: ,向上:常见问题 [目录][索引]

13.7 实现可重定位性

很长一段时间以来,对于许多 GNU 软件包用户来说,这一直是一个痛苦的问题 包不可重定位。这意味着用户无法复制程序, 由同一台计算机上的其他用户安装到其主目录, 并使其正常工作(包括 i18n)。这么多用户需要去 通过它的所有 依赖项、选项和障碍。configure; make; make install

大多数包管理系统,允许用户安装 预建二进制包,解决了“易用性”的 安装“的问题,但它们硬连线路径名,通常为 或 .这意味着用户需要 root 安装二进制包的权限,并阻止安装两个 同一二进制包的不同版本。/usr/usr/local

可以将可重定位的程序移动或复制到其他位置 在文件系统上。可以对已安装的 并移动程序,并通过符号链接调用它们。是的 只有在硬链接的情况下,才可以使用硬链接做同样的事情 链接文件与真实程序位于同一目录中。

若要将程序配置为可重定位,请添加到命令行。--enable-relocatableconfigure

在某些操作系统上,可执行文件会记住共享库的位置 并且更喜欢它们而不是任何其他搜索路径。因此,这样的 可执行文件将首先在原始库中查找其共享库 安装目录,然后才在当前安装中 目录。因此,为了可靠性,最好还给出一个指向不存在的目录的选项 现在和永远不会被创建,例如.您可以在命令行上使用 避免安装到该目录中。--prefix--prefix=/nonexistentDESTDIR=dest-dirmake

我们不建议使用非特权用户可写入的前缀 (例如),因为这样的目录可以重新创建 由非特权用户在删除原始目录后。 我们也不建议使用可能位于自动挂载程序后面的前缀 (例如)因为性能影响 目录搜索。/tmp/inst$$$HOME/inst$$

下面是一个示例安装运行,其中考虑了所有这些因素 建议:

./configure --enable-relocatable --prefix=/nonexistent
make
make install DESTDIR=/tmp/inst$$

安装将不起作用 setuid 或 setgid 可执行文件,因为此类可执行文件仅搜索 出于安全原因的系统库路径。--enable-relocatable

运行时损失和大小损失在 GNU/Linux 上可以忽略不计(只是 当可执行文件启动时,一个系统调用更多),并且很小 其他系统(包装程序只是设置一个环境变量 并执行实际程序)。


下一篇: , 上一篇: 性, 上一篇: 常见问题 [目录][索引]

13.8 我无法构建 Bison

我无法构建野牛,因为找不到抱怨。 我该怎么办?makemsgfmt

像大多数支持国际化的 GNU 软件包一样,该特性 默认情况下处于打开状态。如果在子目录中构建时遇到问题,则表明系统的国际化 缺乏支持。您可以重新配置 Bison 以关闭此支持,也可以安装 GNU 从 https://ftp.gnu.org/gnu/gettext/ 获取文本并重新配置 野牛。有关详细信息,请参阅文件。po--disable-nlsABOUT-NLS

我无法构建 Bison,因为我的 C 编译器太旧了。

除了 GLR 解析器(需要 C99),Bison 生成的 C 代码 只需要 C89 或更高版本。然而,野牛本身需要普通的C99 诸如语句后的声明等功能。Bison 的脚本尝试在默认的编译器上启用 C99(或更高版本)支持 到 C99 之前。如果您的编译器完全缺少这些 C99 功能,GCC 可能会 好吧,是一个更好的选择;或者您可以尝试升级到编译器的最新版本 版本。configure


下一篇: , 上一篇: , 向上: 常见问题 [目录][索引]

13.9 在哪里可以找到帮助?

我在使用 Bison 时遇到问题。在哪里可以找到帮助?

首先,阅读这本精美的手册。除此之外,您还可以向 help-bison@gnu.org 发送邮件。此邮件列表旨在 挤满了愿意回答有关使用 并安装 Bison。请记住,(大多数)人 名单上有他们生活的方面与野牛无关(! 因此,您可能不会立即收到问题的答案。这可以 令人沮丧,但请尽量不要按喇叭;请记住,任何 他们提供的帮助纯粹是自愿的,是出于他们的善意 红桃。


下一篇: , 上一篇: , 上一篇: 常见问题 [目录][索引]

13.10 错误报告

我发现了一个错误。我应该在错误报告中包含哪些内容?

在发送错误报告之前,请确保您使用的是最新的 版本。检查 https://ftp.gnu.org/pub/gnu/bison/ 或其中之一 镜子。请务必在错误报告中包含版本号。如果 该错误存在于最新版本中,但不存在于以前的版本中, 尝试确定不包含该错误的最新版本。

如果 bug 与解析器相关,则应包含最小的语法 你可以演示这个错误。语法文件也应该是 完成(即,我应该能够通过 Bison 运行它,而无需 编辑或添加任何内容)。语法越小越简单, 修复错误会更容易。

包括有关编译环境的信息,包括 操作系统的名称和版本以及编译器的名称和 版本。如果您在编译时遇到问题,还应该包含一个 生成会话的脚本,从调用 开始。根据错误的性质,您可能会被要求 同时发送其他文件(例如 或 )。configureconfig.hconfig.cache

补丁是最受欢迎的,但不是必需的。也就是说,不要犹豫 仅仅因为您无法提供修复程序而发送错误报告。

bug-bison@gnu.org 发送错误报告。


下一篇: , 上一篇: , 上一篇: 常见问题 [目录][索引]

13.11 更多语言

Bison 会支持 C++ 和 Java 吗?怎么样 ?insert your favorite language here

支持 C++、D 和 Java。我们很乐意添加其他语言; 欢迎投稿。


下一篇: , 上一篇: , 上一篇: 常见问题 [目录][索引]

13.12 Beta 测试

成为 beta 测试人员涉及什么?

这并不是很复杂。基本上,您将下载一个测试 发布、编译它,并使用它来构建和运行一两个解析器。后 也就是说,您将提交错误报告或消息 一切都很好。报告成功以及 失败,因为测试版本最终会成为主流版本, 但前提是它们经过充分测试。如果没有人测试,开发是 基本上停止了。

对于 开发人员不容易访问。他们目前可以轻松访问 最新的 GNU/Linux 和 Solaris 版本。有关其他操作的报告 系统尤其受欢迎。


上一篇: , 上一篇: 常见问题 [目录][索引]

13.13 邮件列表

如何加入 help-bison 和 bug-bison 邮件列表?

请参见 https://lists.gnu.org/


下一篇: , 上一篇: , 上一篇: 野牛 [目录][索引]

附录 A 野牛符号

变量:@$

在操作中,规则左侧的位置。 请参阅追踪位置

变量:@n
符号: @n

在操作中,右侧第 -th 符号的位置 的规则。请参阅追踪位置n

在语法中,Bison 生成的用于中间规则操作的非终端符号 具有语义值。请参阅 Midrule 操作翻译

变量:@name
变量:@[name]

在操作中,符号的位置由 . 请参阅追踪位置name

符号: $@n

在语法中,Bison 生成的用于中间规则操作的非终端符号 没有语义价值。请参阅 Midrule 操作翻译

变量:$$

在操作中,规则左侧的语义值。 请参阅操作

变量:$n

在动作中,-th 符号的语义值 规则的右侧。请参阅操作n

变量:$name
变量:$[name]

在操作中,符号的语义值由 . 请参阅操作name

定界符:%%

用于将语法规则部分与 野牛宣言部分或结语。 请参阅 Bison 语法的整体布局

分隔符:%{code%}

“”和“”之间列出的所有代码都是逐字复制的 添加到解析器实现文件。这样的代码构成了 语法文件。参见 Bison 语法大纲%{%}

指令:%?{表达式}

谓词操作。这是一种可能出现在 规则。对表达式进行计算,如果为 false,则会导致语法错误。在 非确定性操作期间的GLR解析器, 这无提示地导致替代解析死亡。在确定性期间 操作,与YYERROR的效果相同。 请参阅使用任意谓词控制分析

构造: /* ... */
构造: ...

注释,如 C/C++ 中的注释。

分隔符:

将规则的结果与其组件分开。请参阅语法规则

定界符:;

终止规则。请参阅语法规则

分隔符: |

分离同一结果非终端的备用规则。 请参阅语法规则

命令:<*>

用于定义默认标记或默认标记。%destructor%printer

请参阅释放丢弃的符号

命令:<>

用于定义 default tagless 或 default tagless 。%destructor%printer

请参阅释放丢弃的符号

符号: $accept

预定义的非终端,其唯一规则是 '',其中是起始符号。请参阅开始符号。它不能 在语法中使用。$accept: start $endstart

指令:%code {code}
指令:%code qualifier {code}

将逐字插入到输出解析器源中 默认位置或位于 指定的位置。 请参见%code 摘要codequalifier

指令:%debug

配备用于调试的分析器。参见野牛宣言摘要

指令:%define 变量
指令:%define 变量
指令:%define 变量 {value}
指令:%define 变量value

定义一个变量来调整 Bison 的行为。请参阅 %define 摘要

指令:%defines
指令:%defines defines-file

的历史名称。 参见野牛宣言摘要%header

指令:%析构函数

指定分析器应如何回收与 丢弃的符号。请参阅释放丢弃的符号

指令:%dprec

Bison 声明,用于为解析时使用的规则分配优先级 解决减少/减少冲突的时间。请参阅编写 GLR 解析器

指令:%empty

Bison 声明声明明确规则具有空 右手边。请参阅空规则

符号: $end

标记令牌流结束的预定义令牌。它不可能 在语法中使用。

符号:错误

为错误恢复保留的令牌名称。此令牌可用于 语法规则,以便允许 Bison 解析器识别 语法而不停止该过程。实际上,一句话 包含错误可能会被识别为有效。在语法错误时, token 成为当前的 Lookahead 令牌。行动 对应的 然后执行,并展望 令牌将重置为最初导致冲突的令牌。 请参阅错误恢复errorerror

指令:%error-verbose

代表“”的过时指令。%define parse.error verbose

指令:%file-prefix prefix

Bison 声明来设置输出文件的前缀。参见野牛宣言摘要

指令:%glr-parser

Bison 声明生成 GLR 解析器。请参阅编写 GLR 解析器

指令:%header

Bison 声明创建一个解析器头文件,通常 适用于扫描仪。参见野牛宣言摘要

指令:%header header-file

与上面相同,但保存在文件中。 参见野牛宣言摘要header-file

指令:%initial-action

在解析之前运行用户代码。请参阅在解析之前执行操作

指令:%language

指定生成的分析器的编程语言。 参见野牛宣言摘要

指令:%left

Bison 声明为令牌分配优先级和左关联性。 请参阅运算符优先级

指令: %lex-param {argument-declaration} ...

Bison 声明指定应接受的其他参数。请参阅纯解析器的调用约定yylex

指令:%merge

Bison 声明,用于将合并函数分配给规则。如果有 reduce/reduce 与具有相同合并函数的规则冲突, 函数应用于两个语义值以获得单个结果。 请参阅编写 GLR 解析器

指令:%name-prefix prefix

被变量淘汰(请参阅同一程序中的多个解析器)。%defineapi.prefix

重命名解析器中使用的外部符号(变量和函数),以便 它们以“而不是”开头。与此相反,不要重命名类型和宏。prefixyyapi.prefix

在 C 解析器中重命名的符号的精确列表是 、 、 、 和 (如果使用位置) 。如果使用 推送解析器、 、 、 和 也将重命名。为 例如,如果使用 '',则名称将变为 、 等。有关 C++ 分析器,请参阅本节中的文档。yyparseyylexyyerroryynerrsyylvalyycharyydebugyyllocyypush_parseyypull_parseyypstateyypstate_newyypstate_delete%name-prefix "c_"c_parsec_lex%define api.namespace

指令:%no-lines

Bison 声明,以避免在 解析器实现文件。参见野牛宣言摘要#line

指令:%nonassoc

Bison 声明,用于为令牌分配优先级和非关联性。 请参阅运算符优先级

指令:%nterm

Bison 声明声明非终端。请参阅非终端符号

指令:%output “file

Bison 声明设置解析器实现文件的名称。 参见野牛宣言摘要

指令: %param {argument-declaration} ...

Bison 声明指定两者都应该接受的其他参数。请参阅 Parser 函数 yyparseyylexyyparse

指令: %parse-param {argument-declaration} ...

Bison 声明指定应接受的其他参数。请参阅 Parser 函数 yyparseyyparse

指令:%prec

Bison 声明,用于为特定规则分配优先级。 请参阅上下文相关优先级

指令:%precedence

Bison 声明为令牌分配优先级,但没有关联性 请参阅运算符优先级

指令:%pure-parser

“”的弃用版本(参见 %define Summary),Bison 对此更谨慎地警告 不合理使用。%define api.pure

指令:%require version

需要 Bison 的 Vison版本或更高版本。请参阅需要 Bison 版本version

指令:%right

Bison 声明,用于为令牌分配优先级和权限关联性。 请参阅运算符优先级

指令:%skeleton

指定要使用的骨架;通常用于发展。 参见野牛宣言摘要

指令:%start

Bison 声明指定开始符号。请参阅开始符号

指令:%token

Bison 声明,用于声明令牌而不指定优先级。 请参阅令牌种类名称

指令:%token-table

Bison声明,用于在解析器实现中包含令牌名称表 文件。参见野牛宣言摘要

指令:%type

Bison 声明来声明符号值类型。请参阅非终端符号

符号: $undefined

预定义的标记,返回的所有未定义值都映射到该标记上。它不能在语法中使用,而是使用 .yylexerror

指令:%union

Bison 声明指定几种可能的语义数据类型 值。见《国际电联宣言》。

宏:YYABORT

Macro 假装发生了不可恢复的语法错误,立即返回 1。未调用错误报告函数。请参阅 Parser 函数 yyparseyyparseyyerror

对于 Java 解析器,此功能将使用 instead 调用。return YYABORT;

宏:YYACCEPT

宏来假装语言的完整话语已经 读取,立即返回 0。 请参阅 Parser 函数 yyparseyyparse

对于 Java 解析器,此功能将使用 instead 调用。return YYACCEPT;

宏:YYBACKUP

宏,用于丢弃解析器堆栈中的值并伪造前瞻 令 牌。请参阅在操作中使用的特殊功能

宏:YYBISON

Bison 的整数版本,例如 3.7.4 版本的 30704。 仅在中定义。在版本 3.7.4 之前,是 定义为 1。yacc.cYYBISON

变量:yychar

外部整数变量,包含 Lookahead 令牌。(在纯解析器中,它是 中的局部变量。错误恢复规则操作可能会检查此变量。 请参阅在操作中使用的特殊功能yyparse

变量:yyclearin

错误恢复规则操作中使用的宏。它清除了以前的 Lookahead 令牌。请参阅错误恢复

宏:YYDEBUG

要定义的宏,用于为分析器配备跟踪代码。请参阅跟踪分析器

变量:yydebug

默认情况下,外部整数变量设置为零。如果给定非零值,解析器将输出有关输入的信息 符号和解析器操作。请参阅跟踪分析器yydebug

值:YYEMPTY

没有前瞻令牌时的伪令牌类型。

值:YYEOF

表示的令牌类型是输入流的末尾。

宏:yyerrok

宏使解析器立即恢复到其正常模式 语法错误后。请参阅错误恢复

宏:YYERROR

立即导致语法错误。此语句引发错误 恢复,就好像解析器本身检测到错误一样;但是,它 不调用 ,也不打印任何消息。如果你 想要打印错误消息,请在之前显式调用 '' 语句。请参阅错误恢复yyerroryyerrorYYERROR;

对于 Java 解析器,此功能将使用 instead 调用。return YYERROR;

函数:yyerror

用户提供的函数,以便在错误时调用。 请参阅错误报告功能 yyerroryyparse

宏:YYFPRINTF

用于在 C 语言中输出运行时跟踪的宏。 请参阅启用跟踪

宏:YYINITDEPTH

用于指定解析器堆栈的初始大小的宏。 请参阅内存管理以及如何避免内存耗尽

功能:yylex

用户提供的词法分析器函数,调用时没有要获取的参数 下一个令牌。请参阅 Lexical Analyzer 函数 yylex

变量:yylloc

应放置行和列的外部变量 与令牌关联的数字。(在纯解析器中,它是局部的 变量,并将其地址传递给 .) 如果您不使用 '' 功能,则可以忽略此变量 语法操作。 请参阅令牌的文本位置。 在语义操作中,它存储 lookahead 令牌的位置。 请参阅操作和位置yylexyyparseyylex@

类型:YYLTYPE

数据类型为 .默认情况下,在 C 中,具有四个成员的结构 (开始/结束行/列)。请参阅位置的数据类型yylloc

变量:yylval

应将语义放置在其中的外部变量 与令牌关联的值。(在纯解析器中,它是局部的 变量,并将其地址传递给 .) 请参阅标记的语义值。 在语义操作中,它存储 lookahead 标记的语义值。 请参阅操作yylexyyparseyylex

宏:YYMAXDEPTH

用于指定解析器堆栈的最大大小的宏。请参阅内存管理以及如何避免内存耗尽

变量:yynerrs

Bison 每次报告语法错误时递增的全局变量。 (在纯解析器中,它是 中的局部变量。在 纯推送解析器,它是 的成员。 请参阅错误报告功能 yyerroryyparseyypstate

宏:YYNOMEM

Macro 假装内存耗尽,通过返回 2 马上。调用错误报告函数。 请参阅 Parser 函数 yyparseyyparseyyerror

功能:yyparse

Bison 生成的解析器函数;调用此函数开始 解析。请参阅 Parser 函数 yyparse

功能:yypstate_delete

删除解析器实例的函数,由 Bison 在推送模式下生成; 调用此函数可删除与解析器关联的内存。 请参见yypstate_delete。调用时不执行任何操作 使用空指针。

功能:yypstate_new

创建解析器实例的函数,由 Bison 在推送模式下生成; 调用此函数以创建新的解析器。 请参见yypstate_new

功能:yypull_parse

Bison 在推送模式下产生的解析器函数;调用此函数 分析输入流的其余部分。 请参见yypull_parse

功能:yypush_parse

Bison 在推送模式下产生的解析器函数;调用此函数 解析单个令牌。 请参见yypush_parse

宏:YYRECOVERING

当解析器生成 1 时,表达式生成 1 正在从语法错误中恢复,否则为 0。 请参阅在操作中使用的特殊功能YYRECOVERING ()

宏:YYSTACK_USE_ALLOCA

用于控制当 C 语言中的确定性解析器需要扩展其堆栈。如果定义为 0, 解析器将用于扩展其堆栈和内存耗尽 如果失败,则发生(请参阅内存管理和如何避免内存耗尽)。如果定义为 1、解析器会使用 .除 0 和 1 之外的值为 保留用于将来的 Bison 扩展。如果未定义,则默认为 0。allocamallocmallocallocaYYSTACK_USE_ALLOCA

在非常常见的情况下,您的代码可能在具有 堆栈有限且堆栈溢出检查不可靠,您应该 设置为不可能导致 调用时,任何目标主机上的未检查堆栈溢出。您可以检查 Bison 的代码 生成以确定正确的数值。这将 需要一些低级实现细节方面的专业知识。YYMAXDEPTHalloca

类型:YYSTYPE

在C语言中,语义值的数据类型; 默认情况下。 已弃用,转而支持变量 。 请参阅语义值的数据类型int%defineapi.value.type

类型:yysymbol_kind_t

语法的所有符号、标记和非终结符的枚举。 请参阅语法错误报告功能yyreport_syntax_error。使用符号种类 内部由解析器,不应与令牌类型混淆: 终端符号的符号类型不等于其代币类型!(除非 '' 被使用。%define api.token.raw

类型:yytoken_kind_t

声明的所有令牌种类的枚举(请参阅令牌种类名称)。这些是 的返回值。他们 不应与符号种类混淆,内部由 解析 器。%tokenyylex

值:YYUNDEF

表示未知令牌的令牌类型。


下一篇: , 上一篇: , 上一篇: 野牛 [目录][索引]

附录 B 词汇表

接受状态

其唯一操作是接受操作的状态。 因此,接受状态是一致的状态。 请参阅了解解析器

Backus-Naur型(BNF;也称为“Backus正常型”)

最初提出的指定上下文无关语法的形式化方法 作者:John Backus,Peter Naur 在他的 1960-01-02 中略有改进 为后来的 Algol 60 报告做出贡献的委员会文件。 请参阅语言和上下文无关语法

一致状态

仅包含一个可能操作的状态。请参阅默认减少

与上下文无关的语法

指定为规则的语法,无论上下文如何都可以应用。 因此,如果有一条规则说整数可以用作 表达式,表达式所在的任何地方都允许使用整数 允许。请参阅语言和上下文无关语法

反例

标记和/或非终端的序列,带有一个点,表示 冲突。该点标记发生冲突的位置。

一个统一的反例是具有两个不同 解析;它的存在证明了语法是模棱两可的。当一个统一 在合理的时间内找不到反例,构建了一个非统一的反例:两个不同的字符串共享前缀 up 到点。

请参阅反例的生成

默认减少

如果当前解析器状态,则解析器应执行的减少 不包含 Lookahead 令牌的其他操作。在允许的解析器中 州,Bison宣布减少,最大的前瞻将是 默认减少并删除该展望集。请参阅默认减少

默认状态

具有默认减少的一致状态。请参阅默认减少

动态分配

在执行期间发生的内存分配,而不是在 编译时或输入函数时。

空字符串

类似于集合论中的空集合,空字符串是 长度为零的字符串。

有限状态堆栈机

具有离散状态的“机器”,据说它存在于 每一刻都在时间里。当机器的输入被处理时, 机器从一个状态移动到另一个状态,由逻辑指定 机器。在解析器的情况下,输入是语言 解析后,状态对应于语法中的各个阶段 规则。请参阅 Bison 解析器算法

广义 LR (GLR)

一种解析算法,可以处理所有与上下文无关的语法,包括那些 不是 LR(1)。它解决了野牛的情况 确定性解析 算法无法通过有效地拆分多个解析器,尝试所有 可能的解析器,并根据其他解析器丢弃那些失败的解析器 正确的上下文。请参阅广义 LR (GLR) 解析

分组

(通常)在语法上可分割的语言结构; 例如,C 语言中的“expression”或“declaration”。 请参阅语言和上下文无关语法

IELR(1) (消除不足 LR(1))

最小 LR(1) 解析器表构造算法。也就是说,给定任何 上下文无关语法,IELR(1) 生成具有完整 规范 LR(1) 的语言识别能力,但几乎相同 解析器状态数为 LALR(1)。这种解析器状态的减少是 通常是一个数量级。更重要的是,因为规范的 LR(1) 在非 LR(1) 的情况下,额外的解析器状态可能包含重复的冲突 语法中,IELR(1) 的冲突数量通常是一个数量级 也更少。这可以大大降低开发 语法。请参阅 LR 表构造

中缀运算符

一个算术运算符,它位于操作数之间 执行一些操作。

输入流

设备或程序之间的连续数据流。

“Token”和“symbol”都重载为表示语法符号 (kind) 或与事件关联的所有解析信息(kind、value、location) 输入中的语法符号。为了消除歧义,

  • 我们使用“token kind”和“symbol kind”来表示语法符号和 在基本编程语言(C、C++、 等)。这些值的类型名称通常为 、 或 、 、 、 取决于编程语言。token_kind_ttoken_kind_typeTokenKind
  • 我们使用“token”和“symbol”,而不使用“kind”一词来表示解析 发生,我们附加“类型”一词来指代 用基本编程语言表示它们。

总而言之:当你看到“种类”时,将“符号”或“标记”解释为表示 语法符号。当你看不到“善良”时(包括当你 请参阅“type”),将“symbol”或“token”解释为表示解析的 符号

LAC(前瞻校正)

一种解决延迟语法错误问题的解析机制 检测,这是由 LR 状态合并、默认值减少和 的使用。延迟的语法错误检测导致 意外的语义操作,在错误中启动错误恢复 句法上下文,以及详细的预期标记列表不正确 语法错误消息。请参见 LAC%nonassoc

语言结构

该语言的典型用法模式之一。例如,其中一个 C 语言的结构是语句。 请参阅语言和上下文无关语法if

左联想性

从左到右分析具有左关联性的运算符: '' 首先计算 '',然后结合 ‘’.请参阅运算符优先级a+b+ca+bc

左递归

其结果符号也是其第一个组件符号的规则;为 示例,''。请参阅递归规则expseq1 : expseq1 ',' exp;

从左到右解析

解析一种语言的句子,通过逐个分析它 从左到右。请参阅 Bison 解析器算法

词汇分析器(扫描仪)

读取输入流并逐个返回令牌的函数。 请参阅 Lexical Analyzer 函数 yylex

词汇搭配

一个标志,由语法规则中的操作设置,它改变了方式 对令牌进行解析。请参阅 Lexical Tie-ins

文字字符串标记

由两个或多个固定字符组成的标记。请参阅符号、终端和非终端

Lookahead 代币

已读取但尚未移动的令牌。请参阅 Lookahead 令牌

拉尔(1)

Bison(像大多数其他解析器一样)的上下文无关语法类 generators)默认可以处理;LR(1) 的子集。 见神秘的冲突

LR(1)

上下文无关语法类,其中最多一个标记 需要 Lookahead 来消除任何输入的解析的歧义。

非终端符号

一个语法符号,代表可以 通过规则以较小的结构表示;在其他 words,一种不是标记的结构。请参阅符号、终端和非终端

解析 器

通过分析来识别语言的有效句子的函数 从词法传递给它的一组标记的语法结构 分析器。

Postfix 运算符

一个算术运算符,放在它所基于的操作数之后 执行一些操作。

减少

将一串非终端和/或终端替换为单个 非终结,根据语法规则。请参阅 Bison 解析器算法

折返

可重入子程序是一个子程序,可以在任何 并联次数,各种之间无干扰 调用。请参阅纯(重入)解析器

反向波兰符号

一种语言,其中所有运算符都是后缀运算符。

右递归

其结果符号也是其最后一个组件符号的规则;为 示例,''。请参阅递归规则expseq1: exp ',' expseq1;

语义学

在计算机语言中,语义由操作指定 针对语言的每个实例,即 每个语句。请参阅定义语言语义

转变

据说解析器在选择分析时会发生变化 来自流的进一步输入,而不是立即减少一些 已经公认的规则。请参阅 Bison 解析器算法

单字符文字

按原样识别和解释的单个字符。 请参阅从正式规则到 Bison 输入

开始符号

代表完整有效话语的非终端符号 正在分析的语言。起始符号通常列为 语言规范中的第一个非终端符号。 请参阅开始符号

符号种类

语法符号的(有限)枚举,由解析器处理。 请参阅符号、终端和非终端

符号表

一种数据结构,其中符号名称和关联数据存储在 解析以允许重复识别和使用现有信息 符号的用法。请参阅多功能计算器:mfcalc

语法错误

解析输入流时遇到的错误,由于无效 语法。请参阅错误恢复

终端符号

语法中没有规则的语法符号,因此是 语法不可分割。它所代表的一段文本是一个标记。 请参阅语言和上下文无关语法

令 牌

一种语言的基本、语法上不可分割的单位。符号 描述语法中的标记是终端符号。的输入 Bison 解析器是来自词法分析器的标记流。 请参阅符号、终端和非终端

代币种类

语法终端的(有限)枚举,由 扫描器。请参阅符号、终端和非终端

无法访问状态

不存在从中转换到的一系列的解析器状态 解析器的开始状态。在冲突期间,一个国家可能变得无法到达 分辨率。请参阅无法访问的状态


下一篇: , 上一篇: , 上一篇: 野牛 [目录][索引]

附录 C GNU 自由文档许可证

版本 1.3, 2008 年 11 月 3 日
Copyright © 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
https://fsf.org/

Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
  1. 序言

    本许可证的目的是制作手册、教科书或其他 在自由的意义上,功能性和实用性文档是自由的:到 确保每个人都有复制和重新分发它的有效自由, 无论是否对其进行修改,无论是商业性的还是非商业性的。 其次,本许可证为作者和出版商保留了一种方式 为他们的工作获得荣誉,同时不被视为负责任 对于其他人所做的修改。

    该许可证是一种“copyleft”,这意味着衍生 文件作品本身必须是同样意义上的自由。它 补充了 GNU 通用公共许可证,这是一个 copyleft 专为自由软件设计的许可证。

    我们设计了此许可证,以便免费将其用于手册 软件,因为自由软件需要自由文档:一个自由的 程序应附带手册,提供与 软件可以。但本许可证不仅限于软件手册; 它可以用于任何文本作品,无论主题或 是否以印刷书籍的形式出版。我们推荐此许可证 主要用于以指导或参考为目的的作品。

  2. 适用性和定义

    本许可证适用于任何媒介中的任何手动或其他作品, 包含版权所有者放置的通知,说明它可以 根据本许可证的条款分发。此类通知授予 全球范围内的免版税许可,期限不受限制,可以使用它 在此规定的条件下工作。下面的“文件”, 指任何此类手册或作品。任何公众成员都是 被许可人,并称呼为“您”。如果您符合以下条件,即表示您接受许可证 以需要许可的方式复制、修改或分发作品 根据版权法。

    文档的“修改版本”是指包含 文档或其部分,可以逐字复制,也可以使用 修改和/或翻译成另一种语言。

    “次要部分”是命名的附录或前言部分 专门处理 文档的发布者或作者对文档的整体 主题(或相关事项),并且不包含任何可能落下的内容 直接在那个整体主题中。(因此,如果文档在 一部分是数学教科书,中学部分可能无法解释 任何数学。这种关系可能是一个历史问题 与主题或相关事项或法律的联系, 商业、哲学、道德或政治立场 他们。

    “不变部分”是某些次要部分,其标题 在通知中被指定为固定部分 表示本文档是根据本许可证发布的。如果 部分不符合上述中学的定义,则不是 允许指定为不变。文档可能包含零 不变部分。如果文档未标识任何不变性 部分,然后就没有了。

    “封面文本”是列出的某些短文本段落, 作为封面文本或封底文本,在通知中说 本文档根据本许可发布。封面文字可能 最多 5 个字,封底文字最多 25 个字。

    文档的“透明”副本是指机器可读的副本, 以规范可用于 适合修改文件的公众 直接使用通用文本编辑器或(对于由 像素)通用的绘画程序或(用于图纸)一些广泛可用的 绘图编辑器,适用于输入文本格式化程序或 用于自动转换为适合输入的各种格式 文本格式化程序。在其他透明文件中制作的副本 其标记或没有标记的格式已被安排为阻止 或劝阻读者的后续修改是不透明的。 如果大量使用图像格式,则图像格式不透明 的文本。不“透明”的副本称为“不透明”。

    适合透明副本的格式示例包括普通格式 无标记的 ASCII、Texinfo 输入格式、LaTeX 输入 格式、SGML 或 XML 使用公开可用的 DTD,以及符合标准的简单 HTML, 专为人工修改而设计的 PostScript 或 PDF。例子 透明图像格式包括 PNG、XCF 和 JPG。不透明格式包括专有格式,这些格式可以是 仅由专有文字处理器、SGML 或 DTD 和/或处理工具的 XML 不普遍可用,以及机器生成的 HTML, PostScript 或 PDF 由某些文字处理器生成 仅用于输出目的。

    “扉页”是指,对于印刷书籍,扉页本身, 加上以下页面,以清晰地保存材料 此许可证要求出现在标题页中。对于作品 没有任何标题页的格式,因此,“标题页”是指 作品标题最显眼的外观附近的文字, 在正文开头之前。

    “出版商”是指分发副本的任何个人或实体 向公众公布该文件。

    “标题为 XYZ”的部分是指文档的命名子单元,其 title 要么精确为 XYZ,要么在括号中包含 XYZ 将 XYZ 翻译成另一种语言的文本。(这里 XYZ 代表 下面提到的特定部分名称,例如“致谢”, “奉献”、“认可”或“历史”。“保留标题” 当您修改文档时,这意味着它仍然是 根据此定义,“标题为 XYZ”部分。

    该文件可能在通知旁边包含保修免责声明,该声明 声明本许可证适用于文档。这些保修 免责声明被视为通过引用包含在本文件中 许可,但仅限于免责保证:任何其他 这些保修免责声明可能具有的暗示是无效的,并且具有 对本许可证的含义没有影响。

  3. 逐字复印

    您可以在任何媒体上复制和分发文档 商业或非商业,前提是本许可、 版权声明,以及说明本许可证适用的许可声明 到文档的所有副本中复制,并且您不添加其他 本许可的任何条件。您不得使用 阻碍或控制读数或进一步阅读的技术措施 复制您制作或分发的副本。但是,您可以接受 补偿以换取副本。如果你分发一个足够大的 份数 您还必须遵守第 3 节中的条件。

    您也可以在上述相同条件下出借副本,并且 您可以公开展示副本。

  4. 批量复印

    如果您发布印刷副本(或通常具有 打印的封面)的文件,编号超过 100 个,以及 文档的许可声明需要封面文本,您必须附上 封面副本,清晰易读地包含所有这些封面 文本:封面上的封面文字和封底上的文字 封底。两个封面也必须清晰易读地识别 您作为这些副本的发布者。前盖必须存在 标题中所有单词均同样突出的完整标题,并且 可见。此外,您还可以在封面上添加其他材料。 复印时更改仅限于封面,只要它们保留 文档的标题并满足这些条件,可以处理 作为其他方面的逐字复制。

    如果任一封面所需的文本太长而无法容纳 清晰地,您应该列出第一个(尽可能多的 合理地)在实际封面上,其余部分继续到相邻的 页面。

    如果发布或分发文档编号的不透明副本 超过 100 个,则必须包含机器可读的透明 与每个不透明副本一起复制,或在每个不透明副本中或与每个不透明副本一起声明 一般网络使用的计算机网络位置 公众可以使用公共标准网络协议进行下载 文档的完整透明副本,不添加任何材料。 如果您使用后一种选择,则必须采取合理谨慎的步骤, 当您开始大量分发不透明副本时,以确保 因此,此透明副本将保留在所述位置的可访问性 位置,直到您上次分发 不透明的副本(直接或通过您的代理商或零售商) 向公众发布版本。

    要求(但不是必需)您联系 在重新分发任何大量副本之前,请做好文档记录,以提供 他们有机会为您提供文档的更新版本。

  5. 修改

    您可以在以下位置复制和分发文档的修改版本 上述第 2 节和第 3 节的条件,前提是您释放 正是本许可证下的修改版本,以及修改后的 版本填充文档的角色,从而许可分发 以及将修改版本修改给拥有副本的人 的。此外,您必须在修改版本中执行以下操作:

    1. 在标题页(以及封面,如果有)中使用不同的标题 来自文档和以前版本的文档 (如果有的话,应该在“历史记录”部分列出 的文件)。您可以使用与以前版本相同的标题 如果该版本的原始发布者授予权限。
    2. 在扉页上列出作者、一个或多个个人或实体 负责修改后的修改的作者 版本,以及至少五位主要作者 文件(其所有主要作者,如果少于五人), 除非他们让您免于此要求。
    3. 在“标题”页上注明 修改版本,作为发布者。
    4. 保留文档的所有版权声明。
    5. 为您的修改添加适当的版权声明 与其他版权声明相邻。
    6. 在版权声明之后立即包括许可声明 授予公众使用修改版本的许可 本许可的条款,格式见以下附录。
    7. 在该许可证通知中保留固定部分的完整列表 以及文档许可声明中给出的所需封面文本。
    8. 包括本许可证的未更改副本。
    9. 保留标题为“历史”的部分,保留其标题,并添加 至少说明标题、年份、新作者和 扉页上给出的修改版本的出版商。如果 文档中没有标题为“历史记录”的部分,请创建一个 将文档的标题、年份、作者和出版商声明为 在其标题页上给出,然后添加一个描述修改的项目 上一句所述的版本。
    10. 保留文档中给出的网络位置(如果有) 公众访问文档的透明副本,同样 文档中给出的先前版本的网络位置 它基于。这些可以放在“历史记录”部分。 您可以省略在以下位置发表的作品的网络位置 至少在文件本身之前四年,或者如果原件 它所引用的版本的发布者授予权限。
    11. 对于标题为“致谢”或“奉献”的任何部分,请保留 该部分的标题,并在该部分中保留所有 每个贡献者致谢和/或的实质和语气 其中给出的奉献。
    12. 保留文档的所有不变部分, 其文本和标题未更改。节号 或等效内容不被视为章节标题的一部分。
    13. 删除标题为“背书”的任何部分。这样的部分 可能不包含在修改版本中。
    14. 不要将任何现有部分的标题改为“背书”或 与任何固定部分的标题冲突。
    15. 保留任何保修免责声明。

    如果修改后的版本包含新的前言部分,或者 符合次要部分条件且不包含任何材料的附录 从文档中复制,您可以选择指定部分或全部 这些部分为不变。为此,请将其标题添加到 修改版本许可声明中的固定部分列表。 这些标题必须与任何其他部分标题不同。

    您可以添加标题为“背书”的部分,前提是它包含 只不过是各种人对您的修改版本的认可 当事方 - 例如,同行评议的声明或文本具有 被组织批准为权威定义 标准。

    您可以添加最多五个单词的段落作为封面文本,以及 最多 25 个单词作为封底文本段落,到列表末尾 修改版的封面文本。只有一段 封面文字和封底文字之一可以通过(或 通过)任何一个实体的安排。如果文档已经 包含您之前添加的同一封面的封面文本,或者 根据您代表的同一实体作出的安排, 您不得添加其他;但您可以明确替换旧的 来自添加旧发布者的上一个发布者的权限。

    文档的作者和发布者不遵守本许可 允许使用他们的名字进行宣传或主张或 暗示对任何修改版本的认可。

  6. 合并文档

    您可以将本文档与根据本文档发布的其他文档合并 许可,根据上文第 4 节中定义的条款进行修改 版本,前提是您在组合中包含所有 所有原始文档的固定部分,未修改,以及 将它们全部列为组合工作的不变部分,在其 许可声明,并且您保留其所有保修免责声明。

    合并的作品只需包含本许可证的一份副本,并且 多个相同的不变部分可以替换为单个 复制。如果有多个同名的固定部分,但 不同的内容,使每个此类部分的标题具有唯一性 在末尾的括号中添加原文的名称 该部分的作者或出版商(如果已知),或者是唯一编号。 对列表中的章节标题进行相同的调整 合并作品的许可通知中的固定部分。

    在组合中,您必须合并标题为“历史记录”的任何部分 在各种原始文件中,形成一个标题为 “历史”;同样,将标题为“致谢”的任何部分合并, 以及标题为“奉献”的任何部分。您必须删除所有 标题为“背书”的部分。

  7. 文件集

    您可以收集由文档和其他文档组成的集合 根据本许可证发布,并替换本许可证的单个副本 使用包含在 集合,前提是您遵守本许可证的规则 在所有其他方面逐字复印每份文件。

    您可以从此类集合中提取单个文档,然后分发 它根据本许可证单独使用,前提是您插入此许可证的副本 许可进入提取的文档,并遵循本许可证 关于逐字复制该文件的其他方面。

  8. 与独立作品的聚合

    本文件或其衍生物与其他单独文件的汇编 以及独立的文件或作品,在存储卷中或存储卷上,或 分发介质,称为“聚合”,如果版权 编译产生的不用于限制合法权利 汇编的用户超出了个人作品的允许范围。 当文档包含在聚合中时,本许可证不会 适用于其他非本身的作品 本文件的衍生作品。

    如果第 3 节的封面文本要求适用于这些 文档的副本,则如果文档少于 整个聚合,文档的封面文本可以放在 涵盖将文档括在聚合中的括号,或 如果文件是电子形式的,则相当于封面的电子等价物。 否则,它们必须出现在括起整个封面的印刷封面上 骨料。

  9. 译本

    翻译被认为是一种修改,所以你可以 根据第 4 节的条款分发文档的翻译。 用翻译替换固定部分需要特殊的 获得其版权所有者的许可,但您可以包括 部分或全部不变部分的翻译,以及 这些固定部分的原始版本。您可以包括 本许可证的翻译,以及 文件和任何保修免责声明,前提是您还包括 本许可证的英文原文和原始版本 这些通知和免责声明。如果两者之间存在分歧 本许可证或声明的翻译和原始版本 或免责声明,以原始版本为准。

    如果文档中的某个部分标题为“致谢”, “奉献”或“历史”,要求(第 4 节)保存 其标题(第 1 节)通常需要更改实际 标题。

  10. 终止

    您不得复制、修改、再许可或分发文档 除非本许可证明确规定。任何尝试 否则,复制、修改、再许可或分发它是无效的,并且 将自动终止您在本许可项下的权利。

    但是,如果您停止所有违反本许可证的行为,则您的许可证 (a)暂时恢复特定版权所有者的版权, 除非并且直到版权所有者明确并最终 终止您的许可,并且 (b) 永久终止,如果版权所有者 未能在以下情况下通过某种合理方式通知您违规行为 戒烟后 60 天。

    此外,您从特定版权所有者处获得的许可是 如果版权所有者通知您 通过一些合理的手段违规,这是你第一次有 收到违反本许可(任何作品)的通知 版权所有者,并且您在 30 天后纠正违规行为 您收到通知。

    终止您在本节项下的权利并不终止 根据 本许可证。如果您的权利已被终止,而不是永久终止 恢复,收到部分或全部相同材料的副本 不授予您任何使用它的权利。

  11. 本许可证的未来修订版

    自由软件基金会可能会发布新的修订版本 不时获得 GNU 自由文档许可证。这样的新 版本在精神上将与当前版本相似,但可能 在细节上有所不同,以解决新的问题或疑虑。请参见 https://www.gnu.org/licenses/

    许可证的每个版本都有一个可区分的版本号。 如果文档指定了此文档的特定编号版本 许可证“或任何更高版本”适用于它,您可以选择 遵循该指定版本的条款和条件,或 已发布的任何更高版本(非草稿) 自由软件基金会。如果文档未指定版本 本许可证的编号,您可以选择任何曾经发布过的版本(不是 作为草案)由自由软件基金会(Free Software Foundation)提供。如果文档 指定代理可以决定将来的哪个版本 可以使用许可证,该代理的公开声明接受 version 永久授权您为 公文。

  12. 重新授权

    “大规模多作者协作网站”(或“MMC 网站”)是指任何 万维网服务器,发布受版权保护的作品,也 为任何人编辑这些作品提供了突出的设施。一个 任何人都可以编辑的公共 wiki 就是这种服务器的一个例子。一个 “大规模多作者协作”(或“MMC”)包含在 网站是指在MMC上发布的任何一组受版权保护的作品 网站。

    “CC-BY-SA”是指知识共享署名-相同方式共享 3.0 由非营利性组织 Creative Commons Corporation 发布的许可证 主要营业地点在旧金山的公司, 加利福尼亚州,以及该许可证的未来 copyleft 版本 由同一组织发布。

    “合并”是指发布或重新发布整个文档或 部分,作为另一份文件的一部分。

    如果 MMC 根据此获得许可,则“有资格获得许可” 许可,以及是否在此许可下首次发布的所有作品 在此 MMC 以外的地方,随后合并为整体 或部分进入 MMC,(1) 没有封面文本或固定部分, (2)在2008年11月1日之前注册成立。

    MMC 站点的运营商可以重新发布站点中包含的 MMC 在 2009 年 8 月 1 日之前的任何时间在同一网站上的 CC-BY-SA 下, 前提是 MMC 有资格获得许可。

附录:如何将本许可证用于您的文档

要在您编写的文档中使用本许可证,请附上 许可证在文档中,并放置以下版权和 扉页后面的许可证声明:

  Copyright (C)  year  your name.
  Permission is granted to copy, distribute and/or modify this document
  under the terms of the GNU Free Documentation License, Version 1.3
  or any later version published by the Free Software Foundation;
  with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
  Texts.  A copy of the license is included in the section entitled ``GNU
  Free Documentation License''.

如果您有固定章节、封面文本和封底文本, 将“替换为...文本“行与此相呼应:

    with the Invariant Sections being list their titles, with
    the Front-Cover Texts being list, and with the Back-Cover Texts
    being list.

如果您有没有封面文本的固定部分,或其他一些 将这三者结合起来,合并这两个备选方案以适应 情况。

如果您的文档包含重要的程序代码示例,我们 建议在您选择的 自由软件许可证,如GNU通用公共许可证, 允许它们在自由软件中使用。


下一篇: , 上一篇: , 上一篇: Bison [内容][索引]

书目

[科贝特 1984]

罗伯特·保罗·科贝特, 编译器错误恢复中的静态语义 博士论文,报告编号UCB/CSD 85/251, 电气工程与计算机科学系,计算机科学系 加州大学伯克利分校分部 (1985年6月)。https://digicoll.lib.berkeley.edu/record/135875

[丹尼 2008]

Joel E. Denny 和 Brian A. Malloy,IELR(1):实用 LR(1) 解析器表 对于非 LR(1) 语法与冲突解决,在 2008 ACM Symposium on Applied Computing (SAC'08), ACM, New York, NY, USA, 第240-245页。https://dx.doi.org/10.1145/1363686.1363747

[丹尼 2010 年 5 月]

Joel E. Denny, PSLR(1): 伪无扫描仪最小 LR(1) Deterministic Parsing of Composite Languages, 博士论文, Clemson 美国南卡罗来纳州克莱姆森大学(2010年5月)。https://tigerprints.clemson.edu/all_dissertations/519/

[Denny 2010年11月]

Joel E. Denny 和 Brian A. Malloy,用于生成的 IELR(1) 算法 用于非 LR(1) 语法的最小 LR(1) 解析表,并解决冲突, in Science of Computer Programming,第 75 卷,第 11 期(11 月 2010),第943-979页。https://dx.doi.org/10.1016/j.scico.2009.08.001

[DeRemer 1982年]

Frank DeRemer 和 Thomas Pennello,LALR 的高效计算(1) Look-Ahead Sets, in ACM Transactions on Programming Languages 和 《系统》,第 4 卷,第 4 期(1982 年 10 月),第 1982 页。 615–649.https://dx.doi.org/10.1145/69622.357187

[伊斯拉迪赛库尔 2015]

奇纳瓦特·伊斯拉迪赛库尔、安德鲁·迈尔斯、 从解析冲突中寻找反例, 在第 36 届 ACM SIGPLAN 会议论文集 编程语言设计与实现 (PLDI '15), ACM,第 555-564 页。https://www.cs.cornell.edu/andru/papers/cupex/cupex.pdf

[约翰逊 1978]

史蒂文·约翰逊, 可移植编译器:理论与实践, 在第五届 ACM SIGACT-SIGPLAN 研讨会论文集 编程语言原理 (POPL '78), 第97-104页。https://dx.doi.org/10.1145/512760.512771

[克努斯 1965]

Donald E. Knuth, On the Translation of Languages from Left to Right, in Information and Control, Vol. 8, Issue 6 (December 1965), pp. 607–639.https://dx.doi.org/10.1016/S0019-9958(65)90426-2

[斯科特 2000]

伊丽莎白·斯科特(Elizabeth Scott),阿德里安·约翰斯通(Adrian Johnstone)和沙姆萨·萨达夫·侯赛因(Shamsa Sadaf Hussain),富田风格的广义LR解析器,皇家霍洛威学院,皇家霍洛威大学 伦敦,计算机科学系,TR-00-12(2000 年 12 月)。https://www.cs.rhul.ac.uk/research/languages/publications/tomita_style_1.ps


上一篇: , 上一篇: 野牛 [目录][索引]

术语索引

跳转到:$   %   /   ; < @ |
A B C D E F G H I K L M N O P Q R S T U V W X Y Z
 
索引项 部分

$
$$: 行动
$$: Java 操作功能
$$: 动作特点
$$: D 动作特点
$$: 符号表
$<typealt>$ Java 操作功能
$<typealt>$ 动作特点
$<typealt>n Java 操作功能
$<typealt>n 动作特点
$@n Midrule 动作翻译
$@n 符号表
$accept 符号表
$end 符号表
$n 行动
$n Java 操作功能
$n 动作特点
$n D 动作特点
$n 符号表
$名称 行动
$名称 符号表
$undefined 符号表
$[名称] 行动
$[名称] 符号表

%
%%: Java 声明摘要
%%: 符号表
%? 语义谓词
%?{表达式} 符号表
%代码 Prologue 备择方案
%代码 Decl 摘要
%代码 Decl 摘要
%代码 Decl 摘要
%代码 %code 摘要
%代码 %code 摘要
%代码 %code 摘要
%代码 Calc++ 解析器
%代码 Java 声明摘要
%代码 符号表
%代码 符号表
%code 导入 %code 摘要
%code 导入 Java 声明摘要
%代码初始化 Java 声明摘要
%代码词法分析器 Java 声明摘要
%code 提供 Prologue 备择方案
%code 提供 Decl 摘要
%code 提供 %code 摘要
%code 要求 Prologue 备择方案
%code 要求 Decl 摘要
%code 要求 %code 摘要
%code 要求 Calc++ 解析器
%代码顶部 Prologue 备择方案
%代码顶部 %code 摘要
%调试 Decl 摘要
%调试 启用跟踪
%调试 符号表
%定义 Decl 摘要
%定义 Decl 摘要
%定义 Decl 摘要
%定义 Decl 摘要
%定义 %define 摘要
%定义 %define 摘要
%定义 %define 摘要
%定义 %define 摘要
%定义 %define 摘要
%定义 %define 摘要
%定义 符号表
%定义 符号表
%定义 符号表
%定义 符号表
%定义 api.filename.type %define 摘要
%定义 api.header.include %define 摘要
%定义 api.header.include %define 摘要
%定义 api.location.file %define 摘要
%定义 api.location.file %define 摘要
%定义 api.location.include %define 摘要
%定义 api.location.include %define 摘要
%定义 api.location.type %define 摘要
%定义 api.location.type 用户定义的位置类型
%定义 api.location.type Java 声明摘要
%定义 api.namespace %define 摘要
%定义 api.namespace C++ 野牛接口
%定义 api.package Java 声明摘要
%定义 api.parser.abstract Java 声明摘要
%define api.parser.annotations Java 声明摘要
%定义 api.parser.class %define 摘要
%定义 api.parser.class Java 声明摘要
%定义 api.parser.extends Java 声明摘要
%定义 api.parser.final Java 声明摘要
%define api.parser.implements Java 声明摘要
%定义 api.parser.public Java 声明摘要
%定义 api.parser.strictfp Java 声明摘要
%定义 api.position.type Java 声明摘要
%定义 api.prefix %define 摘要
%定义 api.prefix Java 声明摘要
%定义 api.pure 纯 Decl
%定义 api.pure %define 摘要
%定义 api.push-pull 推送 Decl
%定义 api.push-pull %define 摘要
%定义 api.push-pull D Push Parser 接口
%定义 api.push-pull Java 推送解析器接口
%定义 api.symbol.prefix %define 摘要
%define api.token.constructor %define 摘要
%define api.token.constructor Calc++ 解析器
%定义 api.token.prefix %define 摘要
%定义api.token.raw %define 摘要
%定义api.token.raw Calc++ 解析器
%定义 api.value.automove %define 摘要
%定义 api.value.type %define 摘要
%定义 api.value.type %define 摘要
%定义 api.value.type Java 声明摘要
%define api.value.type 联合 类型生成
%define api.value.type 变体 Calc++ 解析器
%define api.value.union.name %define 摘要
%define init_throws Java 声明摘要
%define lex_throws Java 声明摘要
%define lr.default-reduction %define 摘要
%define lr.default-reduction 默认减少
%define lr.default-reduction 默认减少
%定义 lr.keep-unreachable-state %define 摘要
%定义 lr.keep-unreachable-state 无法访问的状态
%定义 lr.keep-unreachable-state 无法访问的状态
%定义 lr.type %define 摘要
%定义 lr.type LR工作台结构
%定义 lr.type LR工作台结构
%define parse.assert %define 摘要
%define parse.error %define 摘要
%define parse.error 自定义 语法错误报告功能
%define parse.error 详细说明 错误报告功能
%define parse.error 详细 错误报告功能
%define parse.lac %define 摘要
%define parse.lac 紫胶
%define parse.lac 紫胶
%define parse.trace %define 摘要
%define parse.trace 启用跟踪
%define 投掷 Java 声明摘要
%定义 Decl 摘要
%定义 Decl 摘要
%定义 符号表
%定义 符号表
%析构函数 类型化中间线操作
%析构函数 析构函数 Decl
%析构函数 析构函数 Decl
%析构函数 析构函数 Decl
%析构函数 Decl 摘要
%析构函数 符号表
%dprec 合并 GLR 解析
%dprec 符号表
%空 空规则
%空 符号表
%错误-详细 符号表
%期望 期待 Decl
%期望 Decl 摘要
%期望-rr 简单的 GLR 解析器
%期望-rr 期待 Decl
%期望-rr Decl 摘要
%文件前缀 Decl 摘要
%文件前缀 符号表
%glr-解析器 GLR 解析器
%glr-解析器 简单的 GLR 解析器
%glr-解析器 符号表
%标题 Decl 摘要
%标题 Decl 摘要
%标题 符号表
%标题 符号表
%初始操作 初始操作 Decl
%初始操作 初始操作 Decl
%初始操作 初始操作 Decl
%初始操作 符号表
%语言 Decl 摘要
%语言 符号表
%language “Java” Java 声明摘要
%left 符号定义
%left Decl 摘要
%left 使用优先级
%left 符号表
%lex-参数 纯粹的召唤
%lex-参数 纯粹的召唤
%lex-参数 Java 声明摘要
%lex-参数 符号表
%位置 Decl 摘要
%合并 合并 GLR 解析
%合并 符号表
%name-prefix Decl 摘要
%name-prefix 符号表
%无行 Decl 摘要
%无行 符号表
%nonassoc Decl 摘要
%nonassoc 使用优先级
%nonassoc LR工作台结构
%nonassoc 默认减少
%nonassoc 符号表
%nterm 类型 Decl
%nterm 符号定义
%nterm Decl 摘要
%nterm Java 声明摘要
%nterm 符号表
输出百分比 Decl 摘要
输出百分比 符号表
%参数 纯粹的召唤
%参数 纯粹的召唤
%参数 符号表
%parse-param Parser 函数
%parse-param Parser 函数
%parse-param Java 声明摘要
%parse-param 符号表
%prec 上下文优先级
%prec 符号表
%优先级 使用优先级
%优先级 仅优先
%优先级 符号表
%打印机 打印机 Decl
%打印机 打印机 Decl
%打印机 打印机 Decl
%pure-parser Decl 摘要
%pure-parser 符号表
%require 需要 Decl
%require Decl 摘要
%require 符号表
%正确 Decl 摘要
%正确 使用优先级
%正确 符号表
%骷髅 Decl 摘要
%骷髅 符号表
%开始 开始 Decl
%开始 Decl 摘要
%开始 符号表
%token 代币 Decl
%token 符号定义
%token Decl 摘要
%token Java 声明摘要
%token 符号表
%token-table Decl 摘要
%token-table 符号表
%类型 类型 Decl
%类型 符号定义
%类型 Decl 摘要
%类型 符号表
%union 联合 Decl
%union 结构化价值类型
%union Decl 摘要
%union 符号表
%详细 Decl 摘要
%yacc Decl 摘要
%{: Java 声明摘要
%{code%} 符号表

/
/*: 符号表
/* ... */ 语法大纲
//: 符号表
... 语法大纲

:
 符号表

;
;: 符号表

<
<*>: 析构函数 Decl
<*>: 打印机 Decl
<*>: 符号表
<>: 析构函数 Decl
<>: 打印机 Decl
<>: 符号表

@
@$: 操作和位置
@$: Java 操作功能
@$: 动作特点
@$: 符号表
@n Midrule 动作翻译
@n 操作和位置
@n Java 操作功能
@n 动作特点
@n 动作特点
@n 符号表
@n 符号表
@姓名 操作和位置
@姓名 符号表
@[名称] 操作和位置
@[名称] 符号表

|
| 规则语法
| 符号表

一个
抽象语法树 实现 gotos/loops
接受状态 理解
操作 行动
操作数据类型 操作类型
操作功能摘要 动作特点
Midrule 中的操作 中间线操作
Midrule 中的操作 析构函数 Decl
操作, 位置 操作和位置
动作,语义 语义操作
附加 C 代码部分 结语
解析器的算法 算法
模棱两可的语法 语言和语法
模棱两可的语法 广义 LR 解析
关联性 为什么优先
AST的: 实现 gotos/loops

B
Backus-Naur 形式 语言和语法
地点起点 C++ 位置
开始地点 D 位置值
开始地点 Java 位置值
野牛声明摘要 Decl 摘要
野牛宣言 声明
野牛宣言(介绍): 野牛宣言
野牛语法 Bison 中的语法
Bison 调用 调用
Bison 解析器 Bison 解析器
Bison 解析器算法 算法
野牛符号,表格: 符号表
野牛效用 Bison 解析器
野牛-I18N.M4 启用 I18n
野牛宝 国际化
野牛YYParser的骨架 D 解析器接口
野牛YYParser的骨架 Java 解析器接口
bisonYYParser版本 D 解析器接口
bisonYYParser版本 Java 解析器接口
BISON_I18N 启用 I18n
BISON_LOCALEDIR 启用 I18n
BNF的: 语言和语法
支撑代码 规则语法
BYACC 比亚克

C
C 代码,附加部分 结语
C语言界面 接口
计算值: 中缀 Calc
计算器,中缀表示法 中缀 Calc
计算器,位置跟踪 位置跟踪计算
计算器,多功能 多功能计算器
计算器,简单 RPN 计算
规范 LR 神秘的冲突
规范 LR LR工作台结构
CEX的: 反例
字符令牌 符号
位置列 C++ 位置
位置列 C++ 位置
位置列 C++ 位置
评论 语法大纲
兼容性 版本控制
编译解析器 Rpcalc 编译
冲突反例 反例
冲突 GLR 解析器
冲突 简单的 GLR 解析器
冲突 合并 GLR 解析
冲突 移位/减少
冲突,减少/减少 减少/减少
冲突,禁止显示以下警告 期待 Decl
一致状态 默认减少
上下文 C++ 解析器上下文
上下文相关优先级 上下文优先级
上下文无关语法 语言和语法
控制功能 Rpcalc 主
核心,项目集 理解
反例,非统一 词汇表
反例,统一 词汇表
反例 反例
counter_type C++ 位置

D
晃来晃去的: 移位/减少
位置的数据类型 位置类型
操作中的数据类型 操作类型
语义值的数据类型 值类型
调试 描图
解析器上的debug_level C++ 解析器接口
解析器上的debug_stream C++ 解析器接口
声明摘要 Decl 摘要
声明 序幕
声明部分 序幕
声明,野牛 声明
声明,野牛(介绍) 野牛宣言
声明文字字符串标记 代币 Decl
声明运算符优先级 优先级 Decl
声明开始符号 开始 Decl
声明令牌种类名称 代币 Decl
声明值类型 类型生成
声明值类型 联合 Decl
声明值类型 结构化价值类型
声明值类型,非终端 类型 Decl
默认操作 行动
默认数据类型 值类型
默认位置类型 位置类型
默认减少 默认减少
默认堆栈限制 内存管理
默认开始符号 开始 Decl
默认状态 默认减少
延迟语义操作 GLR 语义操作
定义语言语义 语义学
延迟语法错误检测 LR工作台结构
延迟语法错误检测 默认减少
延迟的 yylex 调用 默认减少
丢弃的符号 析构函数 Decl
丢弃的符号,中间规则操作 类型化中间线操作
 图形可视化
虚线: 理解

E
否则,悬空 移位/减少
emplace<T, U> on value_type C++ 变体
emplace<T> 在 value_type C++ 变体
emplace<T> 在 value_type C++ 变体
空规则 空规则
地点结束 C++ 位置
地点结束 D 位置值
地点结束 Java 位置值
结语 结语
错误 错误恢复
错误 符号表
解析器上的错误 C++ 解析器接口
解析器上的错误 C++ 解析器接口
错误恢复 错误恢复
错误恢复,中间规则操作 类型化中间线操作
错误恢复,简单 简单的错误恢复
错误报告功能 错误报告功能
错误报告例程 Rpcalc 错误
示例,简单 例子
例外情况: C++ 解析器接口
练习 习题
expected_tokens上下文 C++ 解析器上下文

F
文件格式 语法布局
职位文件 C++ 位置
filename_type C++ 位置
有限状态机 解析器状态
形式语法 Bison 中的语法
语法文件的格式 语法布局
释放丢弃的符号 析构函数 Decl
经常问的问题 常见问题

G
广义 LR (GLR) 解析 语言和语法
广义 LR (GLR) 解析 GLR 解析器
广义 LR (GLR) 解析 广义 LR 解析
广义 LR (GLR) 解析,模棱两可的语法 合并 GLR 解析
广义 LR (GLR) 解析,明确的语法 简单的 GLR 解析器
YYParser 上的 getDebugLevel Java 解析器接口
YYParser 上的 getDebugLevel(): D 解析器接口
YYParser 上的 getDebugStream Java 解析器接口
YYParser 上的 getDebugStream(): D 解析器接口
Lexer 上的 getEndPos Java 扫描程序接口
YYParser 上的 getErrorVerbose Java 解析器接口
YYParser 上的 getErrorVerbose(): D 解析器接口
YYParser.Context 上的 getExpectedTokens Java 解析器上下文接口
getExpectedTokens(YYParser.SymbolKind[] on YYParser.Context D 解析器上下文接口
YYParser.Context 上的 getLocation Java 解析器上下文接口
YYParser.Context 上的 getLocation(): D 解析器上下文接口
Lexer 上的 getLVal Java 扫描程序接口
YYParser.SymbolKind 上的 getName Java 解析器上下文接口
Lexer 上的 getStartPos Java 扫描程序接口
获取文本 国际化
YYParser.Context 上的 getToken Java 解析器上下文接口
YYParser.Context 上的 getToken(): D 解析器上下文接口
用语集 词汇表
GLR 解析器和 yychar GLR 语义操作
GLR 解析器和 yyclearin GLR 语义操作
GLR 解析器和 YYERROR: GLR 语义操作
GLR 解析器和 yylloc GLR 语义操作
GLR 解析器和YYLLOC_DEFAULT 位置默认操作
GLR 解析器和 yylval GLR 语义操作
GLR 解析 语言和语法
GLR 解析 GLR 解析器
GLR 解析 广义 LR 解析
GLR 解析,模棱两可的语法 合并 GLR 解析
GLR 解析,明确的语法 简单的 GLR 解析器
GLR 和 LALR LR工作台结构
语法文件 语法布局
语法规则语法 规则语法
语法规则部分 语法规则
语法,野牛 Bison 中的语法
语法,与上下文无关 语言和语法
分组、句法 语言和语法

H
头罩 Decl 摘要
历史 历史

i18n 国际化
YYParser 的 i18n Java 解析器接口
IER系列: 神秘的冲突
IER系列: LR工作台结构
IELR 语法 语言和语法
中缀表示法计算器 中缀 Calc
在位置初始化 C++ 位置
初始化位置 C++ 位置
接口 接口
国际化 国际化
简介 介绍
调用 Bison 调用
项目 理解
项目集核心 理解
项目集核心 理解

K
内核,项集 理解
symbol_type: 完整的符号

L
LAC系列: LR工作台结构
LAC系列: 默认减少
LAC系列: 紫胶
拉尔 神秘的冲突
拉尔 LR工作台结构
LALR 语法 语言和语法
语言语义,定义 语义学
Bison 语法的布局 语法布局
左递归 递归
词汇分析器 词汇的
词汇分析器,用途 Bison 解析器
词汇分析器,写作 Rpcalc Lexer
词汇搭配 词汇搭配
位置线 C++ 位置
位置线 C++ 位置
位置线 C++ 位置
文字字符串标记 符号
文字令牌 符号
地点 地点
地点 跟踪位置
位置操作 操作和位置
上下文中的位置 C++ 解析器上下文
位置: C++ 位置
位置: C++ 位置
位置: C++ 位置
位置: Java 位置值
位置: Java 位置值
位置跟踪计算器 位置跟踪计算
位置,文本 地点
位置,文本 跟踪位置
location_type C++ 解析器接口
前瞻校正 紫胶
展望上下文 C++ 解析器上下文
Lookahead 代币 展望未来
LR 神秘的冲突
LR 语法 语言和语法
ltcalc 位置跟踪计算

M
简单示例中的 main 函数 Rpcalc 主
解析器上的 make_token 完整的符号
解析器上的 make_token 完整的符号
解析器上的 make_token 完整的符号
解析器上的 make_token 完整的符号
内存耗尽 内存管理
内存管理 内存管理
MFCALC 多功能计算器
中间规则操作 中间线操作
中间规则操作 析构函数 Decl
多功能计算器 多功能计算器
多字符文字 符号
相互递归 递归
神秘的冲突 LR工作台结构
神秘的冲突 神秘的冲突

N
symbol_type上的名字 完整的符号
命名引用 命名引用
NLS 国际化
非确定性解析 语言和语法
非确定性解析 广义 LR 解析
非终端符号 符号
非终端,无用 理解
非统一反例 词汇表

O
运算符优先级 优先
运算符优先级,声明 优先级 Decl
operator!= 在位置上 C++ 位置
operator!= on 位置 C++ 位置
解析器上的 operator(): C++ 解析器接口
操作员+在现场 C++ 位置
操作员+在现场 C++ 位置
操作员+在位置 C++ 位置
operator+= 位置 C++ 位置
operator+= 位置 C++ 位置
运算符+= on position C++ 位置
操作员位置 C++ 位置
操作员-位置 C++ 位置
operator-= 在位置上 C++ 位置
operator-= on 位置 C++ 位置
运算符<< C++ 位置
运算符<< C++ 位置
操作员== 在现场 C++ 位置
operator== 在位置上 C++ 位置
调用 Bison 的选项 调用
解析器堆栈溢出 内存管理

P
解析错误 错误报告功能
解析器上的解析 C++ 解析器接口
在 YYParser 上解析 Java 解析器接口
YYParser 上的 parse(): D 解析器接口
解析器 Bison 解析器
解析器上的解析器: C++ 解析器接口
解析器上的解析器: C++ 解析器接口
解析器堆栈 算法
解析器堆栈溢出 内存管理
解析器状态 解析器状态
位置位置 C++ 位置
优先级声明 优先级 Decl
运算符的优先级 优先
优先级,取决于上下文 上下文优先级
precedence,一元运算符 上下文优先级
防止有关冲突的警告 期待 Decl
打印语义值 打印机 Decl
序幕 序幕
序幕 %code 摘要
序幕替代方案 Prologue 备择方案
纯解析器 纯 Decl
推送解析器 推送 Decl
推送解析器 推送 Decl
在 YYParser 上推解析 D Push Parser 接口
YYParser上的push_parse Java 推送解析器接口
YYParser上的push_parse Java 推送解析器接口
YYParser上的push_parse Java 推送解析器接口

Q
问题 常见问题

R
恢复中: Java 操作功能
在 YYParser 上恢复 Java 解析器接口
在 YYParser 上恢复(): D 解析器接口
从错误中恢复 错误恢复
递归规则 递归
减少/减少冲突 减少/减少
减少/减少冲突 GLR 解析器
减少/减少冲突 简单的 GLR 解析器
减少/减少冲突 合并 GLR 解析
减少 算法
可重入解析器 纯 Decl
Lexer 上的 reportSyntaxError Java 扫描程序接口
reportSyntaxError(Lexer 上的 YYParser.Context D 扫描仪接口
解析器上的report_syntax_error C++ 解析器上下文
需要 Bison 版本 需要 Decl
反向波兰符号 RPN 计算
右递归 递归
RP计算 RPN 计算
规则语法 规则语法
规则,虚线 理解
规则,为空 空规则
规则,递归 递归
规则,无用 理解
语法规则部分 语法规则
运行野牛(介绍): Rpcalc 生成

S
语义操作 语义操作
GLR 解析器中的语义谓词 语义谓词
语义值 语义值
语义值类型 值类型
YYParser 上的 setDebugLevel Java 解析器接口
setDebugLevel(int on YYParser D 解析器接口
YYParser 上的 setDebugStream Java 解析器接口
setDebugStream(File on YYParser: D Parser Interface
setErrorVerbose on YYParser Java Parser Interface
setErrorVerbose(YYParser上的布尔值 D 解析器接口
解析器上的set_debug_level C++ 解析器接口
解析器上的set_debug_stream C++ 解析器接口
移位/减少冲突 GLR 解析器
移位/减少冲突 简单的 GLR 解析器
移位/减少冲突 移位/减少
换档 算法
简单的例子 例子
单字符文字 符号
堆栈溢出 内存管理
堆栈、解析器 算法
使用Bison的阶段 阶段
开始符号 语言和语法
开始符号,声明 开始 Decl
状态(解析器): 解析器状态
踩踏位置 C++ 位置
字符串令牌 符号
摘要,动作特点 动作特点
摘要,野牛宣言 Decl 摘要
禁止冲突警告 期待 Decl
符号 符号
符号表示例 Mfcalc 符号表
符号种类 D 解析器上下文接口
符号种类 Java 解析器上下文接口
符号(抽象) 语言和语法
野牛中的符号,表格: 符号表
symbol_kind_type C++ 解析器上下文
解析器上的symbol_name C++ 解析器上下文
symbol_type 完整的符号
解析器上的symbol_type::symbol_type 完整的符号
解析器上的symbol_type::symbol_type 完整的符号
解析器上的symbol_type::symbol_type 完整的符号
解析器上的symbol_type::symbol_type 完整的符号
句法分组 语言和语法
语法错误 错误报告功能
语法规则的语法 规则语法
syntax_error C++ 解析器接口
syntax_error syntax_error C++ 解析器接口
syntax_error syntax_error C++ 解析器接口

T
终端符号 符号
文本位置 地点
文本位置 跟踪位置
this(Lexer on YYParser D 解析器接口
this(lex_param,在 YYParser 上 D 解析器接口
this(位置: D 位置值
this(位置: D 位置值
代币 语言和语法
代币 C++ 解析器接口
代币种类 符号
令牌种类名称,声明 代币 Decl
上下文上的令牌 C++ 解析器上下文
令牌,无用 理解
token_kind_type C++ 解析器接口
toString on Location Java 位置值
toString() 在位置上 D 位置值
跟踪解析器 描图

U
一元运算符优先级 上下文优先级
有蹄类动物 历史
统一反例 词汇表
无法访问的状态 无法访问的状态
无用的非终端 理解
无用规则 理解
无用令牌 理解
使用 Bison 阶段

V
值类型,语义 值类型
值类型,声明 类型生成
值类型,声明 联合 Decl
值类型,声明 结构化价值类型
值类型,非终端,声明 类型 Decl
value, 语义 语义值
value_type C++ 解析器接口
版本 版本控制
版本要求 需要 Decl

W
警告, 防止 期待 Decl
编写词汇分析器 Rpcalc Lexer

X
XML格式 Xml格式

Y
雅克: 雅克
YYABORT Parser 函数
YYABORT Parser 函数
YYABORT 动作特点
YYABORT Java 操作功能
YYABORT 符号表
YYACCEPT接受 Parser 函数
YYACCEPT接受 Parser 函数
YYACCEPT接受 动作特点
YYACCEPT接受 Java 操作功能
YYACCEPT: Table of Symbols
YYBACKUP: Action Features
YYBACKUP: Action Features
YYBACKUP: Table of Symbols
YYBISON: Table of Symbols
yychar: GLR Semantic Actions
yychar: Lookahead
yychar: Action Features
yychar: Table of Symbols
yyclearin: GLR Semantic Actions
yyclearin: Action Features
yyclearin: Error Recovery
yyclearin: Table of Symbols
yydebug: Tracing
YYDEBUG: Enabling Traces
YYDEBUG: Table of Symbols
yydebug: Table of Symbols
YYEMPTY: Action Features
YYEMPTY: Table of Symbols
YYENABLE_NLS: Enabling I18n
YYEOF: Action Features
YYEOF: Table of Symbols
yyerrok: Action Features
yyerrok: Error Recovery
yyerrok: D Action Features
yyerrok: Table of Symbols
YYERROR: GLR Semantic Actions
yyerror: Error Reporting Function
YYERROR: Action Features
YYERROR: Java Action Features
yyerror: Java Action Features
yyerror: Java Action Features
yyerror: Java Action Features
YYERROR: Table of Symbols
yyerror: Table of Symbols
yyerror on Lexer: Java Scanner Interface
yyerror on YYParser: Java Parser Interface
yyerror on YYParser: Java Parser Interface
yyerror on YYParser: Java Parser Interface
yyerror(Location on Lexer: D Scanner Interface
yyerror(Location on YYParser: D Parser Interface
yyerror(string on YYParser: D Parser Interface
YYFPRINTF: Enabling Traces
YYFPRINTF: Table of Symbols
YYINITDEPTH: Memory Management
YYINITDEPTH: Table of Symbols
yylex: Lexical
yylex: Split Symbols
yylex: Split Symbols
yylex: Complete Symbols
yylex: Complete Symbols
yylex: Table of Symbols
yylex on Lexer: Java Scanner Interface
yylex() on Lexer: D Scanner Interface
yylloc: GLR Semantic Actions
yylloc: Actions and Locations
yylloc: Token Locations
yylloc: Lookahead
yylloc: Action Features
yylloc: Table of Symbols
YYLLOC_DEFAULT: Location Default Action
YYLOCATION_PRINT: Printing Locations
YYLOCATION_PRINT 打印位置
YYLTYPE型: 令牌位置
YYLTYPE型: 符号表
yylval GLR 语义操作
yylval 行动
yylval 代币价值
yylval 展望未来
yylval 动作特点
yylval 符号表
YYMAX深度 内存管理
YYMAX深度 符号表
yynerrs 错误报告功能
yynerrs 符号表
YYNOMEM的: Parser 函数
YYNOMEM的: Parser 函数
YYNOMEM的: 动作特点
YYNOMEM的: 符号表
YYO 打印机 Decl
yyparse Parser 函数
yyparse Parser 函数
yyparse 符号表
YYParser on YYParser Java 解析器接口
YYParser on YYParser Java 解析器接口
yypcontext_expected_tokens 语法错误报告功能
yypcontext_location 语法错误报告功能
yypcontext_t 语法错误报告功能
yypcontext_token 语法错误报告功能
yypstate_delete 推送解析器接口
yypstate_delete 推送解析器接口
yypstate_delete 符号表
yypstate_expected_tokens 推送解析器接口
yypstate_new 推送解析器接口
yypstate_new 推送解析器接口
yypstate_new 符号表
yypull_parse 推送解析器接口
yypull_parse 符号表
yypush_parse 推送解析器接口
yypush_parse 推送解析器接口
yypush_parse 符号表
YY恢复 错误恢复
YY恢复 动作特点
YY恢复 动作特点
YY恢复 符号表
yyreport_syntax_error 语法错误报告功能
YYSTACK_USE_ALLOCA 符号表
YYS类型 符号表
yysymbol_kind_t 语法错误报告功能
yysymbol_kind_t 符号表
yysymbol_name 语法错误报告功能
yytoken_kind_t 符号表
YYUNDEF的: 符号表

Z
动物园 野牛

跳转到:$   %   /   ; < @ |
A B C D E F G H I K L M N O P Q R S T U V W X Y Z
 

脚注

(1)

此示例的扩展版本的来源包括 在 C 中作为 提供,在 C++ 中作为 提供。examples/c/glrexamples/c++/glr

(2)

的来源为 。rpcalcexamples/c/rpcalc

(3)

一个类似的例子,但使用明确的语法 比 precedence 和 associativity 注释,可用作 。examples/c/calc

(4)

的来源是 可用作 .mfcalcexamples/c/mfcalc

(5)

但是,通过 C 宏定义不是 推荐的方式。查看位置的数据类型YYLTYPE

(6)

请参见 https://austingroupbugs.net/view.php?id=1388#c5220

(7)

此示例的源代码为 。examples/c++/simple.yy

(8)

Java 解析器将操作包含在单独的 方法,而不是为了有一个直观的语法 对应于这些 C 宏。yyparse

(9)

由于首字母缩略词,名称有时是 给出“YACC”,但约翰逊在描述性论文中使用了“Yacc” 包含在版本中 7 Unix 手册

(10)

https://lists.gnu.org/r/bison-patches/2019-02/msg00061.html